前言 先簡要說說觀察者模式的定義與組成吧。
觀察者模式定於與組成 通俗來講,觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。當這個主題對象在狀態上發生變化時,通知所有觀察者對象,讓它們可以主動的更新自己。觀察者模式主要分為四部分:抽象主題角色;抽象觀察者角色;具體主題角色;具體觀察者角色。
接下來看看我自訂的觀察者模式吧。
自訂觀察者模式 先說說需求吧: 通過觀察者模式來實現一個簡單的訊息訂閱。 接下來說說具體的設計抽象: 想想觀察者的主要部分,大致可以抽象出這樣幾個類:Subject(主題),Observer(觀察者),ConcreateSubject(具體的主題角色),ConcreteObserver(具體的觀察者角色)
抽象出這幾個類後,可以畫出如下的類圖:
具體的代碼實現: 抽象角色:
Subject:
public interface Subject { /** * 訊息訂閱 * @param observer */ public void subscribe(Observer observer); /** * 廣播訊息 */ public void publish(); /** * 退訂某個服務 * @param observer */ public void unSubscribe(Observer observer); /** * 退訂全部服務 */ public void unSubscribeAllServers(); }
AbstractSubject:
public abstract class AbstractSubject implements Subject { //觀察者List private List<Observer> observers; public AbstractSubject() { this.observers = Lists.newArrayList(); } public void subscribe(Observer observer) { if (null == observer) { return; } observers.add(observer); } public void publish() { for (Observer observer : observers) { observer.update(this); } } public void unSubscribe(Observer observer) { System.out.println("unsubscribe...\n"); observers.remove(observer); } public void unSubscribeAllServers() { observers.clear(); }}
Observer:
public interface Observer { public void update(Subject subject);}具體角色:
ConcreateSubject:
public class ConcreateSubject extends AbstractSubject { //訊息狀態 private int status; //訊息主題 private String subject; public ConcreateSubject(String subject) { super(); status = Status.NOT_SEND.getStatus(); this.subject = subject; } //訊息狀態變更 public void changed(int newStatus) { this.status = newStatus; publish(); } public String getSubject() { return subject; } public int getStatus() { return status; }}
ConcreateObserver:
public class ConcreateObserver implements Observer { //觀察者名字 private String name; //訂閱主題 private Subject subject; public ConcreateObserver(String name, Subject subject) { this.name = name; this.subject = subject; subject.subscribe(this); } //一般僅僅只是通知的時候可以換個名字,但是有業務操作的時候就建議用update public void update(Subject subject) { ConcreateSubject concreateSubject = (ConcreateSubject) subject; System.out.println("observerName = " + this.name + "\n" + concreateSubject.getSubject() + " changed...." + "\n" + "status = " + Status.getStatus(concreateSubject.getStatus()).getStatusName() + "\n"); } @Override public String toString() { return "ConcreateObserver{" + "name='" + name + '\'' + ", subject=" + subject + '}'; }}
最後看看具體的調用:
Client:
public class Client { public static void main(String args[]) { //定義一個主題 ConcreateSubject subject = new ConcreateSubject("com.sankuai.sendMessage"); //定義兩個觀察者 ConcreateObserver sendObserver = new ConcreateObserver("send_message", subject); ConcreateObserver reSendMessage = new ConcreateObserver("re_send_message", subject); //更改訊息狀態 subject.changed(Status.SEND.status); }}
後記 在設計觀察者模式的時候一定要注意以下幾點:
首先要明確誰是觀察者誰是被觀察者,只要明確誰是關注對象,問題也就明白了,一般觀察者與被觀察者之間是一對多的關係,一個被觀察者可以有多個觀察者,舉個例子吧,比如一個編輯框,有滑鼠點擊的觀察者,還有內容改變的觀察者,等等。 被觀察者在發送廣播通知的時候,無需指定具體的Observer,Observer可以自己決定是否要訂閱Subject的通知; 被觀察者至少需要有三個方法:添加監聽者,移除監聽者,通知觀察者;觀察者至少有一個方法:更新,更新當前的內容,並作出處理; 最後說說該模式應用情境:a.對一個對象的狀態的更新需要其他對象同步更新,或者一個對象的更新需要依賴於另外的對象更新;b.對象僅需要將自己的更新通知給其他的對象,不需要知道其他對象的細節,比如訊息推送。
推模型和拉模型:
觀察者模式根據其側重的功能還可以分為推模型和拉模型
推模型:被觀察者像向觀察者推送主題相關資訊,不管觀察者是否需要,一般這種模型的實現中,會把唄觀察者對象中的資訊通過update(Object obj)傳遞給觀察者;
拉模型:被觀察者在通知觀察者的時候,只需要傳遞少量資訊。如果觀察者需要更具體的資訊,有觀察者主動到被觀察者對象中擷取,相當於是觀察者到被觀察者對象中拉取資料。一般這種模型的視線中,會把被觀察者對象自身通過update方法傳遞給觀察者update(Subject subject),這樣在觀察者需要擷取資料的時候,就可以通過這個引用來擷取。
JDK中也支援觀察者的模式,具體源碼分析也會在後續的博文中給出。