要想正確理解設(shè)計(jì)模式,首先必須明確它是為了解決什么問題而提出來的。
設(shè)計(jì)模式學(xué)習(xí)筆記,歡迎交流。
——Shulin
轉(zhuǎn)載請注明出處:http://blog.csdn.net/zhshulin
觀察者模式是對象的行為模式,外號非常多...??!又叫發(fā)布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監(jiān)聽器(Source/Listener)模式或從屬者(Dependents)模式。
觀察者模式定義了一種一對多的依賴關(guān)系,讓多個(gè)觀察者對象同時(shí)監(jiān)聽某一個(gè)主題對象。這個(gè)主題對象在狀態(tài)上發(fā)生變化時(shí),會(huì)通知所有觀察者對象,使它們能夠自動(dòng)更新自己。
被觀察者對象在狀態(tài)變化時(shí),通知所有觀察者對象,使它們能夠自動(dòng)更新自己。保證整體的數(shù)據(jù)一致性。此種模式通常被用來實(shí)現(xiàn)事件處理系統(tǒng)。
適用性:
1). 當(dāng)一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面依賴于另一方面。將這二者封裝在獨(dú)立的對 象中以使它們可以各自獨(dú)立地改變和復(fù)用。
2). 當(dāng)對一個(gè)對象的改變需要同時(shí)改變其它對象,而不知道具體有多少對象有待改變。
3). 當(dāng)一個(gè)對象必須通知其它對象,而它又不能假定其它對象是誰。

? 抽象主題(Subject)角色:
抽象主題角色把所有對觀察者對象的引用保存在一個(gè)聚集(比如ArrayList對象)里,每個(gè)主題都可以有任何數(shù)量的觀察者。抽象主題提供一個(gè)接口,可以增加和刪除觀察者對象,抽象主題角色又叫做抽象被觀察者(Observable)角色。
? 具體主題(ConcreteSubject)角色:
將有關(guān)狀態(tài)存入具體觀察者對象;在具體主題的內(nèi)部狀態(tài)改變時(shí),給所有登記過的觀察者發(fā)出通知。具體主題角色又叫做具體被觀察者(Concrete Observable)角色。
? 抽象觀察者(Observer)角色:
為所有的具體觀察者定義一個(gè)接口,在得到主題的通知時(shí)更新自己,這個(gè)接口叫做更新接口。
? 具體觀察者(ConcreteObserver)角色:
存儲(chǔ)與主題的狀態(tài)自恰的狀態(tài)。具體觀察者角色實(shí)現(xiàn)抽象觀察者角色所要求的更新接口,以便使本身的狀態(tài)與主題的狀態(tài) 像協(xié)調(diào)。如果需要,具體觀察者角色可以保持一個(gè)指向具體主題對象的引用。
該實(shí)例模擬了燒水的過程,涉及三個(gè)對象,Heater(熱水器),Display(顯示器),Alarm(報(bào)警器)。模擬過程:為了便于運(yùn)行,水的初始化溫度為90,沸點(diǎn)為100,顯示器依據(jù)熱水器顯示溫度,顯示器顯示溫度為100時(shí),報(bào)警器開始報(bào)警。明顯可以看出Heater是subject ,Display 是它的 Obsrver,同時(shí)Display亦是subject,因?yàn)樗粓?bào)警器觀察,所以Alarm是Display的Observer.

(PS:如果不知道各種符號的意思,看這里——http://blog.csdn.net/zhshulin/article/details/18088633)
Subject:抽象被觀察者
[java] view plain copy PRint?package org.zsl.designmodel.observer; import java.util.ArrayList; import java.util.List; /** * 被觀察者角色 * @author ZSL * */ public abstract class Subject { private List<Observer> list = new ArrayList<Observer>(); /** * 注冊觀察者對象 * @param observer 觀察者 */ public void registerObserver(Observer observer){ list.add(observer); System.out.println("注冊了一個(gè)觀察者角色!"); } /** * 刪除觀察者對象 * @param observer 觀察者 */ public void removeObserver(Observer observer){ list.remove(observer); } /** * 通知所有注冊的觀察者對象,我的狀態(tài)改變咯 * @param newState */ public void notifyAllObservers(String newState){ for(Observer observer : list){ observer.update(newState); } } }Observer :觀察者接口,只有一個(gè)update()方法
[java] view plain copy print?package org.zsl.designmodel.observer; /** * 觀察者接口 * @author ZSL * */ public interface Observer { /** * 更新接口 * @param state 更新的狀態(tài) */ public void update(String state); }Heater:熱水器,一個(gè)具體被觀察者
[java] view plain copy print?package org.zsl.designmodel.observer; public class Heater extends Subject { private int temperature; public int getTemperature() { return temperature; } public void setTemperature(int temperature) { this.temperature = temperature; } public void boilWater(){ for(int i=95;i<105;i++){ temperature = i; this.setTemperature(temperature); //設(shè)置新的溫度 this.notifyAllObservers(Integer.toString(temperature)); //通知所有注冊的觀察者 } } }Display:既是觀察者,又是被觀察者
[java] view plain copy print?package org.zsl.designmodel.observer; /** * 顯示器,既是觀察者又是被觀察者 * @author Administrator * */ public class Display extends Subject implements Observer { private boolean isBoiled = false; //表示水是否燒開了 private int displayTemperature; //定義顯示器的溫度 public boolean isBoiled() { return isBoiled; } public void setBoiled(boolean isBoiled) { this.isBoiled = isBoiled; } @Override public void update(String state) { displayTemperature = Integer.parseInt(state); System.out.println("當(dāng)前顯示器顯示的溫度是:"+displayTemperature); this.displayTemperature(displayTemperature); } private void displayTemperature(int temperature){ if(temperature>100){ this.setBoiled(true); this.notifyAllObservers(Boolean.toString(isBoiled)); } } }Alerm:只是觀察者
[java] view plain copy print?package org.zsl.designmodel.observer; public class Alarm implements Observer { private String observerState; //定義觀察者的狀態(tài) @Override public void update(String state) { Boolean flag = Boolean.parseBoolean(state); if(flag){ System.out.println("報(bào)警器響了,水溫超過100度了。"); } } }測試
[java] view plain copy print?package org.zsl.designmodel.observer; public class Test { public static void main(String[] args) { Heater heater = new Heater(); Display display = new Display(); Alarm alarm = new Alarm(); heater.registerObserver(display); display.registerObserver(alarm); heater.boilWater(); } }結(jié)果:

a、支持松耦合和減少依賴性
客戶端不再依賴于觀察器,因?yàn)橥ㄟ^使用主體和 Observer 接口對客戶端進(jìn)行了隔離。 許多框架具有此優(yōu)點(diǎn),在這些框架中的應(yīng)用程序組件可以注冊為當(dāng)(低級)框架事件發(fā) 生時(shí)得到通知。結(jié)果,框架將調(diào)用應(yīng)用程序組件,但不會(huì)依賴于它。
b、提高了應(yīng)用程序的可維護(hù)性和重用性
面向?qū)ο笤O(shè)計(jì)的一個(gè)原則是:系統(tǒng)中的每個(gè)類將重點(diǎn)放在某一個(gè)功能上,而不是其他方面。一個(gè)對象只做一件事情,并且將他做好。觀察者模式在模塊之間劃定了清晰的界限,提高了應(yīng)用程序的可維護(hù)性和重用性。
c、觀察器數(shù)目可變
觀察器可以在運(yùn)行時(shí)附加和分離,因?yàn)橹黧w對于觀察器數(shù)目沒有任何假定。此功能在這樣的情況下是很有用的:觀察器數(shù)在設(shè)計(jì)時(shí)是未知的。例如,如果用戶在應(yīng)用程序中打開的每個(gè)窗口都需要一個(gè)觀察器。
a、性能降低。
在許多實(shí)現(xiàn)中,觀察器的 update() 方法可能與主體在同一線程中執(zhí)行。如果觀察器列表很長,則執(zhí)行 Notify() 方法可能需要很長時(shí)間。抽取對象依賴性并不意味著添加觀察器對應(yīng)用程序沒有任何影響。
b、內(nèi)存泄漏。
在 Observer 中使用的回調(diào)機(jī)制(當(dāng)對象注冊為以后調(diào)用時(shí))會(huì)產(chǎn)生一個(gè)常見的錯(cuò)誤,從而導(dǎo)致內(nèi)存泄漏,甚至是在托管的 C# 代碼中。假定觀察器超出作用范圍,但忘記取消對主體的訂閱,那么主體仍然保留對觀察器的引用。此引用防止垃圾收集在主體對象也被破壞之前重新分配與觀察器關(guān)聯(lián)的內(nèi)存。如果觀察器的生存期比主體的生存期短得多(通常是這種情況),則會(huì)導(dǎo)致嚴(yán)重的內(nèi)存泄漏。
c、隱藏的依賴項(xiàng)。
觀察器的使用將顯式依賴性(通過方法調(diào)用)轉(zhuǎn)變?yōu)殡[式依賴性(通過觀察器)。如果在整個(gè)應(yīng)用程序中廣泛地使用觀察器,則開發(fā)人員幾乎不可能通過查看源代碼來了解所發(fā)生的事情。這樣,就使得了解代碼更改的含意非常困難。此問題隨傳播級別急劇增大(例如,充當(dāng) Subject 的觀察器)。因此,應(yīng)該僅在少數(shù)定義良好的交互(如 Model-View-Controller 模式中模型和視圖之間的交互)中使用觀察器。最好不要在域?qū)ο笾g使用觀察器。
d、測試 / 調(diào)試?yán)щy。
盡管松耦合是一項(xiàng)重大的體系結(jié)構(gòu)功能,但是它可以使開發(fā)更困難。將兩個(gè)對象去耦的情況越多,在查看源代碼或類的關(guān)系圖時(shí)了解它們之間的依賴性就越難因此,僅當(dāng)可以安全地忽略兩個(gè)對象之間的關(guān)聯(lián)時(shí)才應(yīng)該將它們松耦合(例如,如果觀察器沒有副作用)。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注