當對象間存在一對多關係時,則使用觀察者模式(Observer Pattern)。好比,當一個對象被修改時,則會自動通知它的依賴對象。觀察者模式屬於行爲型模式。java
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並被自動更新。異步
觀察者模式使用四個類 Subject(通用抽象基類,不包含狀態)、ConcreteSubject(帶特定狀態的Subject)、Observer (通用接口,不包含狀態)和 ConcreteObserver (帶特定的Observer )。ide
Subject基類:主要做用是抽象了被觀察目標的通用操做,好比添加觀察者、刪除觀察者、通知全部的觀察者。測試
package com.dotleo.observer; import java.util.ArrayList; import java.util.List; /** * 目標對象,它知道觀察它的觀察者,並提供註冊(添加)和刪除觀察者的接口 * * @author LiuFei * @create 2017-12-03 19:53 */ public class Subject { // 用來保存註冊的觀察者對象 private List<Observer> observers = new ArrayList<Observer>(); // attach detach notifyObservers // 綁定觀察者 public void attach(Observer observer) { observers.add(observer); } // 刪除觀察者 public void detach(Observer observer) { observers.remove(observer); } // 通知全部觀察者 protected void notifyObServers() { for (Observer observer : observers) { observer.update(this); } } }
有了Subject(目標)後,須要特殊定製一個含有狀態的目標供觀察者「觀察」this
ConcreteSubject:持有特定的狀態subjectState
,並繼承Subject基類,擁有綁定觀察者、刪除觀察者、通知全部觀察者的能力。code
package com.dotleo.observer; /** * 具體的目標對象,負責把有關狀態存入到相應的觀察者對象中 * * @author LiuFei * @create 2017-12-03 19:55 */ public class ConcreteSubject extends Subject { // 目標對象的狀態 private String subjectState; public String getSubjectState() { return subjectState; } public void setSubjectState(String subjectState) { this.subjectState = subjectState; this.notifyObServers(); } }
觀察者接口:提供了經過改變的目標去更新本身的狀態server
package com.dotleo.observer; /** * 這是一個觀察者接口,定義一個更新的接口給那些在目標發生變化的時候被通知的對象 * * @author LiuFei * @create 2017-12-03 19:58 */ public interface Observer { void update(Subject subject); }
觀察者實例:實現了接口,擁有特定的狀態,實現能夠和目標同步的方法對象
package com.dotleo.observer; /** * 具體的觀察者對象,實現更新的方法,使自身的狀態和目標的狀態保持一致 * * @author LiuFei * @create 2017-12-03 19:59 */ public class ConcreteObserver implements Observer { // 觀察者的狀態 private String observerState; /** * 獲取目標類的狀態同步到觀察類的狀態中 * @param subject */ @Override public void update(Subject subject) { observerState = ((ConcreteSubject) subject).getSubjectState(); } }
黃明在氣象站工做,他能第一時間獲得天氣預報,爲了給女友和老媽貼心的提示,他打算寫一個程序(他會寫代碼),在天天本身改變天氣狀態後,他的女友和老媽能收到提示:繼承
因而他想到了觀察者模式。接口
Subject基類:WeatherSubject
package com.dotleo.observer.weather; import java.util.ArrayList; import java.util.List; /** * 目標對象,它知道觀察它的觀察者,並提供註冊(添加)和刪除觀察者的接口 * * @author LiuFei * @create 2017-12-03 19:53 */ public class WeatherSubject { // 用來保存註冊的觀察者對象 private List<Observer> observers = new ArrayList<Observer>(); // attach detach notifyObservers /** * 添加訂閱天氣的人 * @param observer */ public void attach(Observer observer) { observers.add(observer); } /** * 刪除訂閱天氣的人 * @param observer */ public void detach(Observer observer) { observers.remove(observer); } /** * 通知訂閱天氣的人 */ protected void notifyObServers() { for (Observer observer : observers) { observer.update(this); } } }
帶有特定狀態(天氣)的Subject:ConcreteWeatherSubject
package com.dotleo.observer.weather; /** * 具體的目標對象,負責把有關狀態存入到相應的觀察者對象中 * * @author LiuFei * @create 2017-12-03 19:55 */ public class ConcreteWeatherSubject extends WeatherSubject { // 目標對象的狀態 private String weatherContent; public String getWeatherContent() { return weatherContent; } public void setWeatherContent(String weatherContent) { this.weatherContent = weatherContent; this.notifyObServers(); } }
Observer接口:Observer
package com.dotleo.observer.weather; /** * 這是一個觀察者接口,定義一個更新的接口給那些在目標發生變化的時候被通知的對象 * * @author LiuFei * @create 2017-12-03 19:58 */ public interface Observer { void update(WeatherSubject subject); }
帶有特定狀態(天氣),信息(觀察者姓名,提醒內容):ConcreteObserver
package com.dotleo.observer.weather; /** * 具體的觀察者對象,實現更新的方法,使自身的狀態和目標的狀態保持一致 * * @author LiuFei * @create 2017-12-03 19:59 */ public class ConcreteObserver implements Observer { // 觀察者姓名,誰收到這個消息,黃明的女友或者老媽 private String observerName; // 天氣狀態,從目標處獲取 private String weatherContext; // 提醒內容,黃明的女友提醒約會,而他老媽則提醒購物 private String remindThing; /** * 獲取目標類的狀態同步到觀察類的狀態中 * @param subject */ @Override public void update(WeatherSubject subject) { weatherContext = ((ConcreteWeatherSubject) subject).getWeatherContent(); System.out.println(observerName + "收到了" + weatherContext + "," + remindThing); } public String getObserverName() { return observerName; } public void setObserverName(String observerName) { this.observerName = observerName; } public String getWeatherContext() { return weatherContext; } public void setWeatherContext(String weatherContext) { this.weatherContext = weatherContext; } public String getRemindThing() { return remindThing; } public void setRemindThing(String remindThing) { this.remindThing = remindThing; } }
測試類:ObserverTest
package com.dotleo.observer.weather; /** * @author LiuFei * @create 2017-12-03 20:33 */ public class ObserverTest { public static void main(String[] args) { // 1. 建立一個目標 ConcreteWeatherSubject weatherSubject = new ConcreteWeatherSubject(); // 2. 建立觀察者 ConcreteObserver concreteObserver1 = new ConcreteObserver(); concreteObserver1.setObserverName("黃明的女友"); concreteObserver1.setRemindThing("是咱們的第一次約會,地點街心公園,不見不散!"); ConcreteObserver concreteObserver2 = new ConcreteObserver(); concreteObserver2.setObserverName("黃明的老媽"); concreteObserver2.setRemindThing("是購物的好日子,明天去商城掃物!"); // 3. 註冊觀察者 weatherSubject.attach(concreteObserver1); weatherSubject.attach(concreteObserver2); // 4. 目標發佈天氣 weatherSubject.setWeatherContent("明每天氣晴朗,藍天白雲,氣溫28度"); } }
運行結果
優勢:
缺點:
在java.util包中包含有基本的Observer接口和Observable抽象類.功能上和Subject接口和Observer接口相似.不過在使用上,就方便多了,由於許多功能好比說註冊,刪除,通知觀察者的那些功能已經內置好了。
Observer -- > Observer接口
Observable --> Subject基類
**ConcreteWeatherSubject **
package com.dotleo.observer.util; import java.util.Observable; public class ConcreteWeatherSubject extends Observable { private String weatherContent; public String getWeatherContent() { return weatherContent; } public void setWeatherContent(String weatherContent) { this.weatherContent = weatherContent; this.setChanged(); this.notifyObservers(); } }
**ConcreteObserver **
package com.dotleo.observer.util; import java.util.Observable; import java.util.Observer; /** * @author LiuFei * @create 2017-12-04 22:37 */ public class ConcreteObserver implements Observer { // 觀察者姓名,誰收到這個消息,黃明的女友或者老媽 private String observerName; // 天氣狀態,從目標處獲取 private String weatherContext; // 提醒內容,黃明的女友提醒約會,而他老媽則提醒購物 private String remindThing; public ConcreteObserver(String observerName, String weatherContext, String remindThing) { this.observerName = observerName; this.weatherContext = weatherContext; this.remindThing = remindThing; } @Override public void update(Observable o, Object arg) { weatherContext = ((ConcreteWeatherSubject ) o).getWeatherContent(); System.out.println(observerName + "收到了" + weatherContext + "," + remindThing); } }
**ObserverTest **
package com.dotleo.observer.util; import com.dotleo.observer.weather.ConcreteObserver; /** * @author LiuFei * @create 2017-12-04 22:43 */ public class ObserverTest { public static void main(String[] args) { // 1. 建立一個目標 com.dotleo.observer.weather.ConcreteWeatherSubject weatherSubject = new com.dotleo.observer.weather.ConcreteWeatherSubject(); // 2. 建立觀察者 com.dotleo.observer.weather.ConcreteObserver concreteObserver1 = new com.dotleo.observer.weather.ConcreteObserver(); concreteObserver1.setObserverName("黃明的女友"); concreteObserver1.setRemindThing("是咱們的第一次約會,地點街心公園,不見不散!"); com.dotleo.observer.weather.ConcreteObserver concreteObserver2 = new ConcreteObserver(); concreteObserver2.setObserverName("黃明的老媽"); concreteObserver2.setRemindThing("是購物的好日子,明天去商城掃物!"); // 3. 註冊觀察者 weatherSubject.attach(concreteObserver1); weatherSubject.attach(concreteObserver2); // 4. 目標發佈天氣 weatherSubject.setWeatherContent("明每天氣晴朗,藍天白雲,氣溫28度"); } }