觀察者模式
觀察者模式也稱為訂閱者模式,實際上我覺得訂閱者更容易理解。這種設(shè)計模式在生活中很常見。比如訂閱期刊雜志、定牛奶等等。我們使用的軟件中也很常見。比如說微博,你關(guān)注了某位明星,其實你就是他的觀察者。每當你關(guān)注的明星發(fā)了新的動態(tài),你就會接收到通知。觀察者模式基于發(fā)布訂閱的方式。訂閱者訂閱目標對象,目標對象維護訂閱者的集合。一旦目標對象狀態(tài)變化,需要通知所有訂閱者,從而觸發(fā)訂閱者的某個行為。
1. 實現(xiàn)觀察者模式
實現(xiàn)觀察者模式,在目標對象中需要維護所有他的觀察者引用。觀察者可以觀察多個不同目標對象的,所以需要讓觀察者知道是哪個目標對象發(fā)送的通知。下面我們通過一個簡單的例子來看看如何實現(xiàn)觀察者模式。
這個例子叫老師點名了。上大學時候,經(jīng)常有同學曠課在宿舍打游戲,并且囑咐去上課的同學,老師要是點名了給我打電話。還好宿舍離教學樓近,接到通知的同學趕緊跑去教室也能趕上。有的膽子大點的同學,接到通知后也不去上課,而是找個關(guān)系好的同學幫忙喊聲到。
去上課的同學是通知者(目標對象),他持有所有需要他通知老師點名的同學(觀察者)的引用,才能在老師點名的時候通知到每個人。程序中我們一般用容器存儲觀察者。當通知的時候循環(huán)調(diào)用所有觀察者暴露出的更新方法。
“老師點名了” 目標對象代碼如下:
public class TeacherRollCallSubject {
private List<Observer> observers = new ArrayList<>();
public void addObserver(Observer observer){
observers.add(observer);
}
public void removeObserver(Observer observer){
observers.remove(observer);
}
public void notifyObservers (){
observers.forEach(Observer::update);
}
}
觀察者只需要實現(xiàn)一個方法,供通知者做通知的時候調(diào)用。我們先定義觀察者的接口。
public interface Observer {
void update();
}
我們定義第一類觀察者的實現(xiàn),他接到通知后,會馬上去教室。
public class GotoClassObserver implements Observer {
@Override
public void update() {
System.out.println("老師點名了!");
System.out.println("我要馬上趕到教室去!");
}
}
第二類觀察者,接到通知后,會通知自己的好朋友幫自己答到。
public class AskForHelpObserver implements Observer {
@Override
public void update() {
System.out.println("老師點名了!");
System.out.println("趕緊給XX發(fā)信息,讓他替我答到!");
}
}
客戶端代碼中,分別聲明兩個不同的觀察者,然后讓這兩個觀察者都觀察老師點名了目標對象。最后出發(fā)目標對象的通知方法??蛻舳舜a如:
public class Client {
public static void main(String[] args) {
Observer studentOne = new GotoClassObserver();
Observer studentTwo = new AskForHelpObserver();
TeacherRollCallSubject subject = new TeacherRollCallSubject();
subject.addObserver(studentOne);
subject.addObserver(studentTwo);
subject.notifyObservers();
}
}
運行后輸出如下:
老師點名了!
我要馬上趕到教室去!
老師點名了!
趕緊給XX發(fā)信息,讓他替我答到!
可以看到每個觀察者都接到了通知,并且按照自己實現(xiàn)的響應方式作出不同的邏輯處理。第一個同學會趕到教室。第二個同學則是給好朋友發(fā)信息,讓其替他答到。
2. 觀察者模式優(yōu)缺點
2.1 優(yōu)點
1、目標對象狀態(tài)的變化,不需要觀察者真的一直觀察。當存在大量觀察者時,如果所有的觀察者都去輪詢狀態(tài),那么系統(tǒng)資源的消耗極大。而觀察者模式避免了這種情況;
2、觀察者模式支持廣播,狀態(tài)變化時,目標對象的所有觀察者都會得到通知;
3、符合開閉原則,目標對象依賴的是觀察者的接口,可以很方便的對觀察者進行擴展,而不需要修改已有觀察者。反過來觀察者也是依賴的目標接口。
2.2 缺點
1、觀察者模式中,觀察者接口限定了方法簽名。有一定的局限性。
3. 觀察者模式適用場景
1、抽象模型可以分為兩個部分,一部分行為取決于另外一部分狀態(tài)的變化。并且你想讓這兩部分各自獨立。每部分都可以獨自使用和復用;
2、一個對象的變化,需要通知其他對象,并且有多少對象需要通知并不清楚
3、你想讓通知與被通知雙方松耦合。
4. 小結(jié)
觀察者模式最大的優(yōu)點就是把目標和觀察者解耦,觀察者根據(jù)目標對象狀態(tài)的變化作出響應。而目標者可以把自己狀態(tài)變化廣播給所有注冊的觀察者。實際使用中有推/拉兩種模型。
- 在推模型中,目標對象會把狀態(tài)改變相關(guān)的所有信息推送出去,信息的量有可能會很大。
- 在拉模型中,目標對象只推送出最核心的信息,比如變化的數(shù)據(jù) id。
觀察者收到消息后再決定如何處理,比如查詢與變化相關(guān)的自己感興趣的數(shù)據(jù)。推模型,目標對象需要知道所有觀察者對數(shù)據(jù)的需求。而拉模型效率會比較差,觀察者收到消息后,還需要自己再去獲取改變的內(nèi)容。關(guān)于推拉模型總結(jié)如下: