設計模式-觀察者模式
1、定義
定義物件的一種一對多/一的依賴關係。當一個物件的狀態發生改變時,所有依賴它的物件都得到通知並被自動更新。
2、實現觀察者模式
觀察者介面:
package com.cn.shejimoshi.guanchazhemoshi; public interface Observer { void update(WeatherSubject weatherSubject); }
觀察者實現類:
package com.cn.shejimoshi.guanchazhemoshi; public class ConcreteObserver implements Observer { private String observerName;//觀察者名稱 public ConcreteObserver(String observerName) { this.observerName = observerName; } /** * 各個觀察者被通知,是通過在主題中呼叫該觀察者的方法實現的 * @param weatherSubject */ public void update(WeatherSubject weatherSubject) { System.out.println("觀察者 "+observerName+" 獲取的資訊是:" +((ConcreteWeatherSubject)weatherSubject).getWeatherContext()); } }
主題:
package com.cn.shejimoshi.guanchazhemoshi; import java.util.ArrayList; import java.util.List; public class WeatherSubject { List<Observer> observers=new ArrayList<Observer>();//維護觀察者的集合 public void addObserver(Observer observer){//新增觀察者到集合 observers.add(observer); } public void notifyObservers(){//通知各個觀察者--實際使用觀察者物件呼叫觀察者的update方法 for (Observer observer: observers){ observer.update(this); } } public List<Observer> getObservers() { return observers; } public void setObservers(List<Observer> observers) { this.observers = observers; } }
主題子類:
package com.cn.shejimoshi.guanchazhemoshi; public class ConcreteWeatherSubject extends WeatherSubject { private String weatherContext; public void setWeatherContext(String weatherContext) {//主題中的weatherContext發生變化,則去通知觀察者 this.weatherContext = weatherContext; this.notifyObservers(); } public String getWeatherContext() { return weatherContext; } }
測試方法:
public class WeatherSubjectTest { @Test public void test(){ //建立觀察者 Observer observer1=new ConcreteObserver("小明"); Observer observer2=new ConcreteObserver("小花"); //建立主題 ConcreteWeatherSubject weatherSubject=new ConcreteWeatherSubject(); //在主題中註冊觀察者 weatherSubject.addObserver(observer1); weatherSubject.addObserver(observer2); //主題變化內容 weatherSubject.setWeatherContext("下雨天留客天天留我不留"); } }
說明:上述例項中,觀察者的update方法引數為主題的例項,該方式是觀察者通過獲取主題的例項,從而獲取主題資訊實現,稱為拉模型(觀察者通過獲取主題的引用去拉取資料);如果update方法的引數為具體的資料,如改成String型別的資料,然後在主題中呼叫時傳入weatherContext,稱為推模型(主題具體資訊推給觀察者)
3、使用java提供的觀察者實現
- 不需要定義主題父類、不需要定義觀察者介面,JDK已提供
- 主題不需要維護觀察者,JDK已提供
- 主題在通知必須呼叫setChanged(),否則通知不起作用
觀察者實現:
package com.cn.shejimoshi.guanchazhemoshi2; import java.util.Observable; import java.util.Observer; public class ConcreteObserver implements Observer { private String observerName; ConcreteObserver(String observerName){ this.observerName=observerName; } //既可以支援推模式,也可以支付拉模式。如果主題使用推模式,則同時支援;如果主題使用拉模式,則只支援拉模式(即arg為空) public void update(Observable o, Object arg) { System.out.println("觀察者"+observerName+" 具體類資訊:"+o); System.out.println("觀察者"+observerName+" 引數資訊:"+arg); } }
主題子類:
package com.cn.shejimoshi.guanchazhemoshi2; import java.util.Observable; public class ConcreteSubject extends Observable { private String context; private int number; public String getContext() { return context; } public void setNumber(int number) { this.number = number; } public void setContext(String context) { this.context = context; this.setChanged(); this.notifyObservers(context); //推模式 //this.notifyObservers();//拉模式 } @Override public String toString() { return "ConcreteSubject{" + "context='" + context + '\'' + ", number=" + number + '}'; } }
測試方法:
public class ConcreteSubjectTest { @Test public void test(){ ConcreteObserver concreteObserver1=new ConcreteObserver("大哈"); ConcreteObserver concreteObserver2=new ConcreteObserver("大牛"); ConcreteSubject concreteSubject=new ConcreteSubject(); concreteSubject.addObserver(concreteObserver1); concreteSubject.addObserver(concreteObserver2); concreteSubject.setNumber(121); concreteSubject.setContext("我在世界之巔"); } }