每一個角色都對應這一個類,好比觀察者模式,觀察者對應着觀察者類,被觀察者對應着被觀察者類。實際上,設計模式就是經過面向對象的特性,將這些角色解耦java
觀察者模式本質上就是一種訂閱 / 發佈的模型,從邏輯上來講就是一對多的依賴關係。什麼意思呢?比如是一羣守衛盯着一個囚犯,只要囚犯一有異動,守衛就必須立刻採起行動(也有多是更新狀態,本質上也是一種行動),那麼守衛就是觀察者,囚犯就是被觀察者設計模式
在一個系統中,實現這種一對多的並且之間有必定關聯的邏輯的時候,因爲須要保持他們之間的協同關係,因此最簡便的方法是採用緊耦合,把這些對象綁定到一塊兒。可是這樣一來,一旦有擴展或者修改的時候,開發人員所面對的難度很是大,並且很容易形成Bug。那麼觀察者模式就解決了這麼一個問題,在保持一系列觀察者和被觀察者對象協同工做的同時,把解耦了它們函數
抽象觀察者角色類this
public interface Observer { // 更新接口 public void update(); }
具體觀察者角色類設計
public class ConcreteObserver implements Observer { // 觀察者的狀態 private String observerState = "Initial"; // 觀察者初始狀態,會隨着被觀察者變化而變化 private String name; // 觀察者名稱,用於標記不一樣觀察者 private Subject concreteSubject; // 構造觀察者,並傳入被主題對象,以及標識該觀察者名稱 public ConcreteObserver(Subject concreteSubject, String name) { this.concreteSubject = concreteSubject; this.name = name; System.out.println("我是觀察者" + name +", 個人狀態是" + observerState); } // 觀察者狀態隨主題主題改變 public void update() { observerState = concreteSubject.SubjectState; System.out.println("我是觀察者" + name +", 個人狀態是" + observerState); } }
抽象主題角色類code
import java.util.List; public abstract class Subject { // 用來保存註冊的觀察者對象 List<Observer> list = null; String SubjectState; // 註冊觀察者對象 public void attach(Observer observer){}; //刪除觀察者對象 public void detach(Observer observer){}; // 通知全部註冊的觀察者對象 public void nodifyObservers(String newState){}; }
具體主題角色類server
import java.util.ArrayList; import java.util.List; public class ConcreteSubject extends Subject { private List<Observer> list = new ArrayList<Observer>(); public String SubjectState; // 註冊觀察者對象 public void attach(Observer observer) { list.add(observer); System.out.println("Attached an observer"); } //刪除觀察者對象 public void detach(Observer observer){ list.remove(observer); } // 通知全部註冊的觀察者對象 public void nodifyObservers(String newState) { for(Observer observer : list) { observer.update(); } } }
客戶端對象
public class Client { public static void main(String[] args) { // 建立主題對象 Subject concreteSubject = new ConcreteSubject(); concreteSubject.attach(new ConcreteObserver(concreteSubject, "安倍晴明")); concreteSubject.attach(new ConcreteObserver(concreteSubject, "神樂")); concreteSubject.attach(new ConcreteObserver(concreteSubject, "源博雅")); concreteSubject.SubjectState = "結界突破!"; concreteSubject.nodifyObservers(concreteSubject.SubjectState); } }
運行結果接口
我是觀察者安倍晴明, 個人狀態是Initial Attached an observer 我是觀察者神樂, 個人狀態是Initial Attached an observer 我是觀察者源博雅, 個人狀態是Initial Attached an observer 我是觀察者安倍晴明, 個人狀態是結界突破! 我是觀察者神樂, 個人狀態是結界突破! 我是觀察者源博雅, 個人狀態是結界突破!
在主題(被觀察者)中,定義了一個集合用來存放觀察者,編寫了註冊attach()和移除detach()觀察者的方法,這體現了一對多的關係,也提供了能夠控制觀察者的方式
關鍵點1:每一個觀察者須要被保存到主題(被觀察者)的集合中,而且被觀察者提供添加和刪除的方式事件
觀察者和被觀察者之間的交互活動。在添加一個觀察者時,把被主題(被觀察者)對象以構造函數的形式給傳入了觀察者。最後主題(被觀察者)執行nodifyObservers()方法,觸發全部觀察者的update()方法以更新狀態
關鍵點2:被主題(被觀察者)把本身傳給觀察者,當狀態改變後,經過遍歷或循環的方式逐個通知列表中的觀察者
但這裏有個問題,主題(被觀察者)是經過構造函數參數的形式,傳給觀察者的,而觀察者對象時被attach()到主題(被觀察者)的list中
關鍵點3:雖然解耦了觀察者和主題(被觀察者)的依賴,讓各自的變化不大影響另外一方的變化,可是這種解耦並不完全,沒有徹底解除二者之間的耦合
關鍵點4:在事件中,訂閱者和發佈者之間是經過把事件處理程序綁定到委託,並非把自身傳給對方。因此解決了觀察者模式中不徹底解耦的問題
觀察者模式,必然涉及到2委託和事件這兩種類型
委託就是可把方法當作另外一個方法參數來傳遞,須要注意方法簽名。委託能夠看作是方法的抽象,也就是方法的「類」,一個委託的實例能夠是一個或者多個方法。咱們能夠經過+=或者-=把方法綁定到委託或者從委託移除
事件是一種特殊的委託。首先事件也是委託,只是在聲明事件的時候,須要加上event,若是你用reflector去看一個事件,你會發現裏面就3樣東西,一個Add_xxxx方法,一個Remove_xxx方法,一個委託。和上面所定義主題(被觀察者)時的註冊attach()和移除detach()有些聯繫
實際上.Net的事件機制就是觀察者模式的一種體現,而且是利用委託來實現。本質上事件就是一種訂閱-發佈模型也就是觀察者模式,這種機制中包含2個角色,一個是發佈者,一個是訂閱者。發佈者類也就相似於主題(被觀察者),發佈者類包含事件和委託定義,以及其之間的關係,發佈者類的對象調用事件通知其餘訂閱者。而訂閱者類也就相似於觀察者,觀察者接受事件,而且提供處理的邏輯。也就是說,訂閱者對象(觀察者)中的方法會綁定到發佈者(被觀察者)對象的委託中,一旦發佈者(被觀察者)中事件被調用,發佈者(被觀察者)就會調用委託中綁定的訂閱者(觀察者)的處理邏輯或者說是處理程序,這就是經過觀察者模式實現的事件
在普通的觀察者模式中,解耦並不完全,那麼在事件的發佈訂閱模型中,解耦完全嗎?爲何?
答案是確定的。由於在事件中,訂閱者和發佈者之間是經過把事件處理程序綁定到委託,並非把自身傳給對方。因此解決了觀察者模式中不徹底解耦的問題
經過委託綁定方法來實現觀察者模式,會不會有什麼隱患?
有的,經過+=去把方法綁定到委託,很容易忘記-=。若是隻綁定不移除,這個方法會一直被引用。咱們知道GC去回收的時候,只會處理沒有被引用的對象,只要是還被引用的對象時不會被回收掉的。因此若是在長期不關閉的系統中(好比監控系統),大量的代碼使用+=而不-=,運行時間長之後有可能會內存溢出
事件,委託,觀察者模式之間的關係
委託是一種類型,事件是一種特殊的委託,觀察者模式是一種設計模式,事件的機制是觀察者模式的一種實現,其中訂閱者和發佈者經過委託實現協同工做