觀察者模式:在觀察者模式中,存在著物件之間的一對多的依賴關係,即一個物件的狀態發生改變時,所有依賴於該物件的物件都會得到通知,並對自身的狀態進行更新;
觀察者模式的學習中,物件之間的一對多的依賴關係是學習觀察者模式的切入點,而被依賴物件(目標物件)的狀態改變會對依賴物件(觀察者物件)狀態產生影響是觀察者模式的關鍵所在;只有物件之間形成一對多的依賴關係,才能實現被依賴物件與依賴物件之間的狀態更新的互動作用;
生活中,其實存在很多觀察者模式的應用場景,比如天氣預報簡訊通知,當你在手機上開通了簡訊通知天氣服務之後,每天你都會收到當天的天氣資訊的簡訊通知;再比如,你的手機上安裝了騰訊體育APP,那麼你只要接受該APP提供的推送即時訊息通知的服務,那麼一旦有更新的體育資訊,將會向你的手機推送相關資訊;
下面通過一個生活中的場景,進行相關的觀察者模式設計程式碼:
package com.pattern.observer;
import java.util.ArrayList;
import java.util.List;
/**
* 抽象目標類
* @author Administrator
*/
public class Subject {
private List<Observer> list = new ArrayList<Observer>();
public void add(Observer o)
{
list.add(o);
}
public void delete(Observer o)
{
list.remove(o);
}
public void notifyAllObservers()
{
for (Observer observer : list)
{
observer.update(this);
}
}
}
package com.pattern.observer;
/**
* 具體目標類
* @author Administrator
*/
public class WeatherSubject extends Subject {
private String information;
public String getInformation()
{
return information;
}
public void setInformation(String information)
{
this.information = information;
this.notifyAllObservers();
}
}
package com.pattern.observer;
/**
* 觀察者介面
* @author Administrator
*/
public interface Observer {
public abstract void update(Subject subject);
}
package com.pattern.observer;
/**
* 具體觀察者類
* @author Administrator
*/
public class ConcreteObserver implements Observer {
private String observerName;
private String information;
private String hint;
public ConcreteObserver(String name,String hint)
{
observerName = name;
this.hint = hint;
}
@Override
public void update(Subject subject)
information =((WeatherSubject)subject).getInformation();
System.out.println(observerName+"收到了"+information+"的通知"+","+hint);
}
}
package com.pattern.observer;
public class TestObserverPattern {
public static void main(String[] args) {
WeatherSubject subject = new WeatherSubject();
ConcreteObserver observerOne = new ConcreteObserver("Tom", "已收到,考慮是否去購物");
ConcreteObserver observerTwo = new ConcreteObserver("Jake", "已收到,考慮是否去旅遊");
subject.add(observerOne);
subject.add(observerTwo);
subject.setInformation("天氣晴朗");
}
}
在設計觀察者模式的時候,由於對於向觀察者物件傳送的訊息內容的不同,觀察者模式又分為推模型和拉模型;
拉模型:將目標物件傳給觀察者物件,觀察者物件需要目標物件的哪一部分的狀態資訊,就通過該目標物件獲取資訊;當觀察者物件對更新資訊的需求發生變化時,拉模型可以很好地應對變化的場景;在上述的例子中,採用的是拉模型進行觀察者模型的設計;
推模型:將指定的更新資訊通知給觀察者物件,當觀察者對更新資訊有了不同的要求之後,這種方式將會顯得難以適應變化的要求;
如果想將上述例子通過推模型實現,只需要對具體觀察者類中的update進行修改和對具體目標類中的notifyAllObservers進行重寫;修改如下:
public void notifyAllObservers()
{
for (Observer observer : list)
{
//將傳入觀察者物件的update方法
observer.update(information);
}
}
public void update(String information)
{
this.information = information;
System.out.println(observerName+"收到了"+information+"的通知"+","+hint);
}
在Java中,其實提供了觀察者模式的實現,利用java.util包下的Observable類和Observer介面就可以大大簡化我們自己設計觀察者模式的步驟;利用Java提供的觀察者模式的實現將上述例子重新進行設計:
/**
*目標類的設計
*/
package com.pattern.observerInJava;
import java.util.Observable;
public class ConcreteSubject extends Observable
{
private String weatherInfor;
public String getWeatherInfor()
{
return weatherInfor;
}
public void setWeatherInfor(String weatherInfor)
{
this.weatherInfor = weatherInfor;
this.setChanged();
this.notifyObservers();
this.notifyObservers(weatherInfor);
}
}
/**
*觀察者類的設計
*/
package com.pattern.observerInJava;
import java.util.Observable;
import java.util.Observer;
public class ConcreteObserver1 implements Observer
{
private String observerName;
private String weatherInfor;
public ConcreteObserver1(String name)
{
observerName = name;
}
@Override
public void update(Observable o, Object arg)
{
weatherInfor = ((ConcreteSubject)o).getWeatherInfor();
System.out.println(observerName+"從目標處拉取了更新的資訊");
System.out.println(observerName+"收到了"+weatherInfor);
System.out.println(observerName+"接收到了推動的更新的資訊");
System.out.println(observerName+"收到了"+weatherInfor);
}
}
/**
*測試類的設計
*/
package com.pattern.observerInJava;
import com.pattern.observerInJava.ConcreteObserver1;
public class TestObserverInJava
{
public static void main(String[] args)
{
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver1 observerOne = new ConcreteObserver1("Tom");
ConcreteObserver1 observerTwo = new ConcreteObserver1("Jake");
subject.addObserver(observerOne);
subject.addObserver(observerTwo);
subject.setWeatherInfor("天氣晴朗");
}
}
進一步地提出問題,當不同的觀察者物件對目標物件的更新資訊中感興趣的方面不同,比如觀察者物件A只對下雨天和陰天感興趣,觀察者物件B只對下雨天感興趣,這時,將需要根據不同的需求向不同的觀察者物件傳送相應的更新資訊;
這裡可以使用面向介面程式設計的思想進行解決,設計一個抽象目標類,在該類中實現其具體實現類中公有的部分,而將notifyAllObservers方法放在其具體實現類中進行實現,在該方法中根據需求的不同進行不同的處理;
notifyAllObservers方法的具體實現可參考如下程式碼:
public void notifyAllObservers()
{
for (Observer observer : list)
{
//如果天氣為下雨天,則只向觀察者A、B推送更新資訊
if("下雨天".equals(weatherInfor))
{
if(observer.getObserverName.equals("A"))
{
observer.update(this);
}
if(observer.getObserverName.equals("B"))
{
observer.update(this);
}
}
//如果天氣為陰天,則指向觀察者A推送更新資訊
if("陰天".equals(weatherInfor))
{
if(observer.getObserverName.equals("A"))
{
observer.update(this);
}
}
}
}
在當自己進行觀察者模式設計的時候,這裡給出一些適用於觀察者模式設計的注意點:
①命名上的建議:
目標介面定義為Subject
觀察者介面定義為Observer
觀察者介面中的的更新方法定義為update
②觸發通知的時機應該在更新完目標物件的狀態資訊之後通知
③通知的順序:觀察者之間是平行的,在觀察者模式中不需要指定通知的順序
觀察者模式設計流程分析:
準備階段 1、建立目標物件 2、建立觀察者物件 3、在目標物件中註冊觀察者物件
執行階段 1、改變目標物件的狀態 2、通知所有已註冊觀察者物件進行相應的處理 3、回撥目標物件,獲取相應資料
觀察者優點及適用場景:
優點:
①觀察者模式實現了觀察者和目標之間的抽象耦合;
②觀察者模式實現了動態聯動;
③觀察者模式支援廣播通訊;
使用場景:
①在一個業務場景中,某一方面的操作需要依賴另一方面狀態的改變;
②在更改一個物件的狀態同時,需要連帶著修改其他物件的狀態,並且需要被連帶修改的物件的個數不知道;
③當需要實現通知物件和被通知物件之間的鬆散耦合的時候,選用觀察者模式;