【學(xué)習(xí)難度:★★★☆☆,使用頻率:★★★★★】
直接出處:觀察者模式
梳理和學(xué)習(xí):https://github.com/BruceOuyang/boy-design-pattern
簡書日期: 2018/03/27
簡書首頁:http://www.lxweimin.com/p/0fb891a7c5ed
對象間的聯(lián)動——觀察者模式(一)
觀察者模式是設(shè)計模式中的“超級模式”,其應(yīng)用隨處可見,在之后幾篇文章里,我將向大家詳細介紹觀察者模式。
“紅燈停,綠燈行”,在日常生活中,交通信號燈裝點著我們的城市,指揮著日益擁擠的城市交通。當紅燈亮起,來往的汽車將停止;而綠燈亮起,汽車可以繼續(xù)前行。在這個過程中,交通信號燈是汽車(更準確地說應(yīng)該是汽車駕駛員)的觀察目標,而汽車是觀察者。隨著交通信號燈的變化,汽車的行為也將隨之而變化,一盞交通信號燈可以指揮多輛汽車。如圖22-1所示:
在軟件系統(tǒng)中,有些對象之間也存在類似交通信號燈和汽車之間的關(guān)系,一個對象的狀態(tài)或行為的變化將導(dǎo)致其他對象的狀態(tài)或行為也發(fā)生改變,它們之間將產(chǎn)生聯(lián)動,正所謂“觸一而牽百發(fā)”。為了更好地描述對象之間存在的這種一對多(包括一對一)的聯(lián)動,觀察者模式應(yīng)運而生,它定義了對象之間一種一對多的依賴關(guān)系,讓一個對象的改變能夠影響其他對象。本章我們將學(xué)習(xí)用于實現(xiàn)對象間聯(lián)動的觀察者模式。
22.1 多人聯(lián)機對戰(zhàn)游戲的設(shè)計
Sunny軟件公司欲開發(fā)一款多人聯(lián)機對戰(zhàn)游戲(類似魔獸世界、星際爭霸等游戲),在該游戲中,多個玩家可以加入同一戰(zhàn)隊組成聯(lián)盟,當戰(zhàn)隊中某一成員受到敵人攻擊時將給所有其他盟友發(fā)送通知,盟友收到通知后將作出響應(yīng)。
Sunny軟件公司開發(fā)人員需要提供一個設(shè)計方案來實現(xiàn)戰(zhàn)隊成員之間的聯(lián)動。
Sunny軟件公司開發(fā)人員通過對系統(tǒng)功能需求進行分析,發(fā)現(xiàn)在該系統(tǒng)中戰(zhàn)隊成員之間的聯(lián)動過程可以簡單描述如下:
聯(lián)盟成員受到攻擊-->發(fā)送通知給盟友-->盟友作出響應(yīng)。
如果按照上述思路來設(shè)計系統(tǒng),由于聯(lián)盟成員在受到攻擊時需要通知他的每一個盟友,因此每個聯(lián)盟成員都需要持有其他所有盟友的信息,這將導(dǎo)致系統(tǒng)開銷較大,因此Sunny公司開發(fā)人員決定引入一個新的角色——“戰(zhàn)隊控制中心”——來負責(zé)維護和管理每個戰(zhàn)隊所有成員的信息。當一個聯(lián)盟成員受到攻擊時,將向相應(yīng)的戰(zhàn)隊控制中心發(fā)送求助信息,戰(zhàn)隊控制中心再逐一通知每個盟友,盟友再作出響應(yīng),如圖22-2所示:
在圖22-2中,受攻擊的聯(lián)盟成員將與戰(zhàn)隊控制中心產(chǎn)生聯(lián)動,戰(zhàn)隊控制中心還將與其他盟友產(chǎn)生聯(lián)動。
如何實現(xiàn)對象之間的聯(lián)動?如何讓一個對象的狀態(tài)或行為改變時,依賴于它的對象能夠得到通知并進行相應(yīng)的處理?
別著急,本章所介紹的觀察者模式將為對象之間的聯(lián)動提供一個優(yōu)秀的解決方案,下面就讓我們正式進入觀察者模式的學(xué)習(xí)。
對象間的聯(lián)動——觀察者模式(二)
22.2 觀察者模式概述
觀察者模式是使用頻率最高的設(shè)計模式之一,它用于建立一種對象與對象之間的依賴關(guān)系,一個對象發(fā)生改變時將自動通知其他對象,其他對象將相應(yīng)作出反應(yīng)。在觀察者模式中,發(fā)生改變的對象稱為觀察目標,而被通知的對象稱為觀察者,一個觀察目標可以對應(yīng)多個觀察者,而且這些觀察者之間可以沒有任何相互聯(lián)系,可以根據(jù)需要增加和刪除觀察者,使得系統(tǒng)更易于擴展。
觀察者模式定義如下: 觀察者模式(Observer Pattern):定義對象之間的一種一對多依賴關(guān)系,使得每當一個對象狀態(tài)發(fā)生改變時,其相關(guān)依賴對象皆得到通知并被自動更新。觀察者模式的別名包括發(fā)布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監(jiān)聽器(Source/Listener)模式或從屬者(Dependents)模式。觀察者模式是一種對象行為型模式。
觀察者模式結(jié)構(gòu)中通常包括觀察目標和觀察者兩個繼承層次結(jié)構(gòu),其結(jié)構(gòu)如圖22-3所示:
在觀察者模式結(jié)構(gòu)圖中包含如下幾個角色:
Subject(目標):目標又稱為主題,它是指被觀察的對象。在目標中定義了一個觀察者集合,一個觀察目標可以接受任意數(shù)量的觀察者來觀察,它提供一系列方法來增加和刪除觀察者對象,同時它定義了通知方法notify()。目標類可以是接口,也可以是抽象類或具體類。
ConcreteSubject(具體目標):具體目標是目標類的子類,通常它包含有經(jīng)常發(fā)生改變的數(shù)據(jù),當它的狀態(tài)發(fā)生改變時,向它的各個觀察者發(fā)出通知;同時它還實現(xiàn)了在目標類中定義的抽象業(yè)務(wù)邏輯方法(如果有的話)。如果無須擴展目標類,則具體目標類可以省略。
Observer(觀察者):觀察者將對觀察目標的改變做出反應(yīng),觀察者一般定義為接口,該接口聲明了更新數(shù)據(jù)的方法update(),因此又稱為抽象觀察者。
ConcreteObserver(具體觀察者):在具體觀察者中維護一個指向具體目標對象的引用,它存儲具體觀察者的有關(guān)狀態(tài),這些狀態(tài)需要和具體目標的狀態(tài)保持一致;它實現(xiàn)了在抽象觀察者Observer中定義的update()方法。通常在實現(xiàn)時,可以調(diào)用具體目標類的attach()方法將自己添加到目標類的集合中或通過detach()方法將自己從目標類的集合中刪除。
觀察者模式描述了如何建立對象與對象之間的依賴關(guān)系,以及如何構(gòu)造滿足這種需求的系統(tǒng)。觀察者模式包含觀察目標和觀察者兩類對象,一個目標可以有任意數(shù)目的與之相依賴的觀察者,一旦觀察目標的狀態(tài)發(fā)生改變,所有的觀察者都將得到通知。作為對這個通知的響應(yīng),每個觀察者都將監(jiān)視觀察目標的狀態(tài)以使其狀態(tài)與目標狀態(tài)同步,這種交互也稱為發(fā)布-訂閱(Publish-Subscribe)。觀察目標是通知的發(fā)布者,它發(fā)出通知時并不需要知道誰是它的觀察者,可以有任意數(shù)目的觀察者訂閱它并接收通知。 下面通過示意代碼來對該模式進行進一步分析。首先我們定義一個抽象目標Subject,典型代碼如下所示:
import java.util.*;
abstract class Subject {
//定義一個觀察者集合用于存儲所有觀察者對象
protected ArrayList observers<Observer> = new ArrayList();
//注冊方法,用于向觀察者集合中增加一個觀察者
public void attach(Observer observer) {
observers.add(observer);
}
//注銷方法,用于在觀察者集合中刪除一個觀察者
public void detach(Observer observer) {
observers.remove(observer);
}
//聲明抽象通知方法
public abstract void notify();
}
具體目標類ConcreteSubject是實現(xiàn)了抽象目標類Subject的一個具體子類,其典型代碼如下所示:
class ConcreteSubject extends Subject {
//實現(xiàn)通知方法
public void notify() {
//遍歷觀察者集合,調(diào)用每一個觀察者的響應(yīng)方法
for(Object obs:observers) {
((Observer)obs).update();
}
}
}
抽象觀察者角色一般定義為一個接口,通常只聲明一個update()方法,為不同觀察者的更新(響應(yīng))行為定義相同的接口,這個方法在其子類中實現(xiàn),不同的觀察者具有不同的響應(yīng)方法。抽象觀察者Observer典型代碼如下所示:
interface Observer {
//聲明響應(yīng)方法
public void update();
}
在具體觀察者ConcreteObserver中實現(xiàn)了update()方法,其典型代碼如下所示:
class ConcreteObserver implements Observer {
//實現(xiàn)響應(yīng)方法
public void update() {
//具體響應(yīng)代碼
}
}
在有些更加復(fù)雜的情況下,具體觀察者類ConcreteObserver的update()方法在執(zhí)行時需要使用到具體目標類ConcreteSubject中的狀態(tài)(屬性),因此在ConcreteObserver與ConcreteSubject之間有時候還存在關(guān)聯(lián)或依賴關(guān)系,在ConcreteObserver中定義一個ConcreteSubject實例,通過該實例獲取存儲在ConcreteSubject中的狀態(tài)。如果ConcreteObserver的update()方法不需要使用到ConcreteSubject中的狀態(tài)屬性,則可以對觀察者模式的標準結(jié)構(gòu)進行簡化,在具體觀察者ConcreteObserver和具體目標ConcreteSubject之間無須維持對象引用。如果在具體層具有關(guān)聯(lián)關(guān)系,系統(tǒng)的擴展性將受到一定的影響,增加新的具體目標類有時候需要修改原有觀察者的代碼,在一定程度上違反了“開閉原則”,但是如果原有觀察者類無須關(guān)聯(lián)新增的具體目標,則系統(tǒng)擴展性不受影響。
思考
觀察者模式是否符合“開閉原則”?【從增加具體觀察者和增加具體目標類兩方面考慮。】
對象間的聯(lián)動——觀察者模式(三)
23.3 完整解決方案
為了實現(xiàn)對象之間的聯(lián)動,Sunny軟件公司開發(fā)人員決定使用觀察者模式來進行多人聯(lián)機對戰(zhàn)游戲的設(shè)計,其基本結(jié)構(gòu)如圖22-4所示:
在圖22-4中,AllyControlCenter充當目標類,ConcreteAllyControlCenter充當具體目標類,Observer充當抽象觀察者,Player充當具體觀察者。完整代碼如下所示:
import java.util.*;
//抽象觀察類
interface Observer {
public String getName();
public void setName(String name);
public void help(); //聲明支援盟友方法
public void beAttacked(AllyControlCenter acc); //聲明遭受攻擊方法
}
//戰(zhàn)隊成員類:具體觀察者類
class Player implements Observer {
private String name;
public Player(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
//支援盟友方法的實現(xiàn)
public void help() {
System.out.println("堅持住," + this.name + "來救你!");
}
//遭受攻擊方法的實現(xiàn),當遭受攻擊時將調(diào)用戰(zhàn)隊控制中心類的通知方法notifyObserver()來通知盟友
public void beAttacked(AllyControlCenter acc) {
System.out.println(this.name + "被攻擊!");
acc.notifyObserver(name);
}
}
//戰(zhàn)隊控制中心類:目標類
abstract class AllyControlCenter {
protected String allyName; //戰(zhàn)隊名稱
protected ArrayList<Observer> players = new ArrayList<Observer>(); //定義一個集合用于存儲戰(zhàn)隊成員
public void setAllyName(String allyName) {
this.allyName = allyName;
}
public String getAllyName() {
return this.allyName;
}
//注冊方法
public void join(Observer obs) {
System.out.println(obs.getName() + "加入" + this.allyName + "戰(zhàn)隊!");
players.add(obs);
}
//注銷方法
public void quit(Observer obs) {
System.out.println(obs.getName() + "退出" + this.allyName + "戰(zhàn)隊!");
players.remove(obs);
}
//聲明抽象通知方法
public abstract void notifyObserver(String name);
}
//具體戰(zhàn)隊控制中心類:具體目標類
class ConcreteAllyControlCenter extends AllyControlCenter {
public ConcreteAllyControlCenter(String allyName) {
System.out.println(allyName + "戰(zhàn)隊組建成功!");
System.out.println("----------------------------");
this.allyName = allyName;
}
//實現(xiàn)通知方法
public void notifyObserver(String name) {
System.out.println(this.allyName + "戰(zhàn)隊緊急通知,盟友" + name + "遭受敵人攻擊!");
//遍歷觀察者集合,調(diào)用每一個盟友(自己除外)的支援方法
for(Object obs : players) {
if (!((Observer)obs).getName().equalsIgnoreCase(name)) {
((Observer)obs).help();
}
}
}
}
編寫如下客戶端測試代碼:
class Client {
public static void main(String args[]) {
//定義觀察目標對象
AllyControlCenter acc;
acc = new ConcreteAllyControlCenter("金庸群俠");
//定義四個觀察者對象
Observer player1,player2,player3,player4;
player1 = new Player("楊過");
acc.join(player1);
player2 = new Player("令狐沖");
acc.join(player2);
player3 = new Player("張無忌");
acc.join(player3);
player4 = new Player("段譽");
acc.join(player4);
//某成員遭受攻擊
Player1.beAttacked(acc);
}
}
編譯并運行程序,輸出結(jié)果如下:
金庸群俠戰(zhàn)隊組建成功!
----------------------------
楊過加入金庸群俠戰(zhàn)隊!
令狐沖加入金庸群俠戰(zhàn)隊!
張無忌加入金庸群俠戰(zhàn)隊!
段譽加入金庸群俠戰(zhàn)隊!
楊過被攻擊!
金庸群俠戰(zhàn)隊緊急通知,盟友楊過遭受敵人攻擊!
堅持住,令狐沖來救你!
堅持住,張無忌來救你!
堅持住,段譽來救你!
在本實例中,實現(xiàn)了兩次對象之間的聯(lián)動,當一個游戲玩家Player對象的beAttacked()方法被調(diào)用時,將調(diào)用AllyControlCenter的notifyObserver()方法來進行處理,而在notifyObserver()方法中又將調(diào)用其他Player對象的help()方法。Player的beAttacked()方法、AllyControlCenter的notifyObserver()方法以及Player的help()方法構(gòu)成了一個聯(lián)動觸發(fā)鏈,執(zhí)行順序如下所示:
Player.beAttacked() --> AllyControlCenter.notifyObserver() -->Player.help()。
對象間的聯(lián)動——觀察者模式(四)
22.4 JDK對觀察者模式的支持
觀察者模式在Java語言中的地位非常重要。在JDK的java.util包中,提供了Observable類以及Observer接口,它們構(gòu)成了JDK對觀察者模式的支持。如圖22-5所示:
(1) Observer接口
在java.util.Observer接口中只聲明一個方法,它充當抽象觀察者,其方法聲明代碼如下所示:
void update(Observable o, Object arg);
當觀察目標的狀態(tài)發(fā)生變化時,該方法將會被調(diào)用,在Observer的子類中將實現(xiàn)update()方法,即具體觀察者可以根據(jù)需要具有不同的更新行為。當調(diào)用觀察目標類Observable的notifyObservers()方法時,將執(zhí)行觀察者類中的update()方法。
(2) Observable類
java.util.Observable類充當觀察目標類,在Observable中定義了一個向量Vector來存儲觀察者對象,它所包含的方法及說明見表22-1:
我們可以直接使用Observer接口和Observable類來作為觀察者模式的抽象層,再自定義具體觀察者類和具體觀察目標類,通過使用JDK中的Observer接口和Observable類,可以更加方便地在Java語言中應(yīng)用觀察者模式。
對象間的聯(lián)動——觀察者模式(五)
22.5 觀察者模式與Java事件處理
JDK 1.0及更早版本的事件模型基于職責(zé)鏈模式,但是這種模型不適用于復(fù)雜的系統(tǒng),因此在JDK 1.1及以后的各個版本中,事件處理模型采用基于觀察者模式的委派事件模型(DelegationEvent Model, DEM),即一個Java組件所引發(fā)的事件并不由引發(fā)事件的對象自己來負責(zé)處理,而是委派給獨立的事件處理對象負責(zé)。
在DEM模型中,目標角色(如界面組件)負責(zé)發(fā)布事件,而觀察者角色(事件處理者)可以向目標訂閱它所感興趣的事件。當一個具體目標產(chǎn)生一個事件時,它將通知所有訂閱者。事件的發(fā)布者稱為事件源(Event Source),而訂閱者稱為事件監(jiān)聽器(Event Listener),在這個過程中還可以通過事件對象(Event Object)來傳遞與事件相關(guān)的信息,可以在事件監(jiān)聽者的實現(xiàn)類中實現(xiàn)事件處理,因此事件監(jiān)聽對象又可以稱為事件處理對象。事件源對象、事件監(jiān)聽對象(事件處理對象)和事件對象構(gòu)成了Java事件處理模型的三要素。事件源對象充當觀察目標,而事件監(jiān)聽對象充當觀察者。以按鈕點擊事件為例,其事件處理流程如下:
(1) 如果用戶在GUI中單擊一個按鈕,將觸發(fā)一個事件(如ActionEvent類型的動作事件),JVM將產(chǎn)生一個相應(yīng)的ActionEvent類型的事件對象,在該事件對象中包含了有關(guān)事件和事件源的信息,此時按鈕是事件源對象;
(2) 將ActionEvent事件對象傳遞給事件監(jiān)聽對象(事件處理對象),JDK提供了專門用于處理ActionEvent事件的接口ActionListener,開發(fā)人員需提供一個ActionListener的實現(xiàn)類(如MyActionHandler),實現(xiàn)在ActionListener接口中聲明的抽象事件處理方法actionPerformed(),對所發(fā)生事件做出相應(yīng)的處理;
(3) 開發(fā)人員將ActionListener接口的實現(xiàn)類(如MyActionHandler)對象注冊到按鈕中,可以通過按鈕類的addActionListener()方法來實現(xiàn)注冊;
(4) JVM在觸發(fā)事件時將調(diào)用按鈕的fireXXX()方法,在該方法內(nèi)部將調(diào)用注冊到按鈕中的事件處理對象的actionPerformed()方法,實現(xiàn)對事件的處理。
使用類似的方法,我們可自定義GUI組件,如包含兩個文本框和兩個按鈕的登錄組件LoginBean,可以采用如圖22-6所示設(shè)計方案:
圖22-6中相關(guān)類說明如下:
(1) LoginEvent是事件類,它用于封裝與事件有關(guān)的信息,它不是觀察者模式的一部分,但是它可以在目標對象和觀察者對象之間傳遞數(shù)據(jù),在AWT事件模型中,所有的自定義事件類都是java.util.EventObject的子類。
(2) LoginEventListener充當抽象觀察者,它聲明了事件響應(yīng)方法validateLogin(),用于處理事件,該方法也稱為事件處理方法,validateLogin()方法將一個LoginEvent類型的事件對象作為參數(shù),用于傳輸與事件相關(guān)的數(shù)據(jù),在其子類中實現(xiàn)該方法,實現(xiàn)具體的事件處理。
(3) LoginBean充當具體目標類,在這里我們沒有定義抽象目標類,對觀察者模式進行了一定的簡化。在LoginBean中定義了抽象觀察者LoginEventListener類型的對象lel和事件對象LoginEvent,提供了注冊方法addLoginEventListener()用于添加觀察者,在Java事件處理中,通常使用的是一對一的觀察者模式,而不是一對多的觀察者模式,也就是說,一個觀察目標中只定義一個觀察者對象,而不是提供一個觀察者對象的集合。在LoginBean中還定義了通知方法fireLoginEvent(),該方法在Java事件處理模型中稱為“點火方法”,在該方法內(nèi)部實例化了一個事件對象LoginEvent,將用戶輸入的信息傳給觀察者對象,并且調(diào)用了觀察者對象的響應(yīng)方法validateLogin()。
(4) LoginValidatorA和LoginValidatorB充當具體觀察者類,它們實現(xiàn)了在LoginEventListener接口中聲明的抽象方法validateLogin(),用于具體實現(xiàn)事件處理,該方法包含一個LoginEvent類型的參數(shù),在LoginValidatorA和LoginValidatorB類中可以針對相同的事件提供不同的實現(xiàn)。
對象間的聯(lián)動——觀察者模式(六)
22.6 觀察者模式與MVC
在當前流行的MVC(Model-View-Controller)架構(gòu)中也應(yīng)用了觀察者模式,MVC是一種架構(gòu)模式,它包含三個角色:模型(Model),視圖(View)和控制器(Controller)。其中模型可對應(yīng)于觀察者模式中的觀察目標,而視圖對應(yīng)于觀察者,控制器可充當兩者之間的中介者。當模型層的數(shù)據(jù)發(fā)生改變時,視圖層將自動改變其顯示內(nèi)容。如圖22-7所示:
在圖22-7中,模型層提供的數(shù)據(jù)是視圖層所觀察的對象,在視圖層中包含兩個用于顯示數(shù)據(jù)的圖表對象,一個是柱狀圖,一個是餅狀圖,相同的數(shù)據(jù)擁有不同的圖表顯示方式,如果模型層的數(shù)據(jù)發(fā)生改變,兩個圖表對象將隨之發(fā)生變化,這意味著圖表對象依賴模型層提供的數(shù)據(jù)對象,因此數(shù)據(jù)對象的任何狀態(tài)改變都應(yīng)立即通知它們。同時,這兩個圖表之間相互獨立,不存在任何聯(lián)系,而且圖表對象的個數(shù)沒有任何限制,用戶可以根據(jù)需要再增加新的圖表對象,如折線圖。在增加新的圖表對象時,無須修改原有類庫,滿足“開閉原則”。
擴展
大家可以查閱相關(guān)資料對MVC模式進行深入學(xué)習(xí),如Oracle公司提供的技術(shù)文檔《Java SE Application Design With MVC》,參考鏈接:http://www.oracle.com/technetwork/articles/javase/index-142890.html。
22.7 觀察者模式總結(jié)
觀察者模式是一種使用頻率非常高的設(shè)計模式,無論是移動應(yīng)用、Web應(yīng)用或者桌面應(yīng)用,觀察者模式幾乎無處不在,它為實現(xiàn)對象之間的聯(lián)動提供了一套完整的解決方案,凡是涉及到一對一或者一對多的對象交互場景都可以使用觀察者模式。觀察者模式廣泛應(yīng)用于各種編程語言的GUI事件處理的實現(xiàn),在基于事件的XML解析技術(shù)(如SAX2)以及Web事件處理中也都使用了觀察者模式。
1.主要優(yōu)點
觀察者模式的主要優(yōu)點如下:
(1) 觀察者模式可以實現(xiàn)表示層和數(shù)據(jù)邏輯層的分離,定義了穩(wěn)定的消息更新傳遞機制,并抽象了更新接口,使得可以有各種各樣不同的表示層充當具體觀察者角色。
(2) 觀察者模式在觀察目標和觀察者之間建立一個抽象的耦合。觀察目標只需要維持一個抽象觀察者的集合,無須了解其具體觀察者。由于觀察目標和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。
(3) 觀察者模式支持廣播通信,觀察目標會向所有已注冊的觀察者對象發(fā)送通知,簡化了一對多系統(tǒng)設(shè)計的難度。
(4) 觀察者模式滿足“開閉原則”的要求,增加新的具體觀察者無須修改原有系統(tǒng)代碼,在具體觀察者與觀察目標之間不存在關(guān)聯(lián)關(guān)系的情況下,增加新的觀察目標也很方便。
2.主要缺點
觀察者模式的主要缺點如下:
(1) 如果一個觀察目標對象有很多直接和間接觀察者,將所有的觀察者都通知到會花費很多時間。
(2) 如果在觀察者和觀察目標之間存在循環(huán)依賴,觀察目標會觸發(fā)它們之間進行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。
(3) 觀察者模式?jīng)]有相應(yīng)的機制讓觀察者知道所觀察的目標對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標發(fā)生了變化。
3.適用場景
在以下情況下可以考慮使用觀察者模式:
(1) 一個抽象模型有兩個方面,其中一個方面依賴于另一個方面,將這兩個方面封裝在獨立的對象中使它們可以各自獨立地改變和復(fù)用。
(2) 一個對象的改變將導(dǎo)致一個或多個其他對象也發(fā)生改變,而并不知道具體有多少對象將發(fā)生改變,也不知道這些對象是誰。
(3) 需要在系統(tǒng)中創(chuàng)建一個觸發(fā)鏈,A對象的行為將影響B(tài)對象,B對象的行為將影響C對象……,可以使用觀察者模式創(chuàng)建一種鏈式觸發(fā)機制。
練習(xí)
Sunny軟件公司欲開發(fā)一款實時在線股票軟件,該軟件需提供如下功能:當股票購買者所購買的某支股票價格變化幅度達到5%時,系統(tǒng)將自動發(fā)送通知(包括新價格)給購買該股票的所有股民。試使用觀察者模式設(shè)計并實現(xiàn)該系統(tǒng)。
練習(xí)會在我的github上做掉