一、概述 觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新。觀察者模式有時成為發佈/訂閱模式,就是讓多個對象在一個對象的狀態改變時被通知到。 二、解決問題 當一個系統有多個類協同工作,如果在一個類中需要知道另外一個類的實現細節才能讓系統運轉, ...
一、概述
觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新。觀察者模式有時成為發佈/訂閱模式,就是讓多個對象在一個對象的狀態改變時被通知到。
二、解決問題
當一個系統有多個類協同工作,如果在一個類中需要知道另外一個類的實現細節才能讓系統運轉,就會導致系統耦合過緊,以後相互依賴的類改變了或者新增了依賴的類,很多類需要同時更改。為了讓交互對象之間能夠松耦合,我們可以使用觀察者模式。
三、結構類圖
四、成員角色
抽象主題(Subject):把所有對觀察者對象的引用保存到一個集合中,每個抽象主題角色都有任何數量的觀察者。抽象主題角色提供一個介面,可以增加和刪除觀察者。一般用一個抽象類和介面實現。
抽象觀察者(Observer):為所有具體觀察者定義一個介面,在得到主題的通知時更新自己。
具體主題(ConcreteSubject):繼承抽象主題,在具體主題內部狀態改變時,給所有登記過的觀察者發出通知。
具體觀察者(ConcreteObserver):實現抽象觀察者所要求的更新介面,以便使本身的狀態與主題的狀態相協調。通常用一個子類實現。如果需要,具體觀察者可以保存一個指向具體主題的引用。
五、應用實例
下麵是銀行賬號資金變動下發通知的例子,只要賬號資金變動,就會同時下發app客戶端消息、郵件消息、簡訊消息通知客戶賬號資金變動了
//主題介面
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
//抽象主題
abstract public class AbstractSubject implements Subject{
//可以在這裡新增Subject沒有的方法,以便子類共用
}
//觀察者介面
public interface Observer {
public void update(double money);
}
//app客戶端觀察者
public class APPObserver implements Observer{
public void update(double money){
//發送app客戶端通知賬號資金變動
System.out.println("app客戶端提醒:您的賬戶資金變動:" + money);
}
}
//郵件觀察者
public class MailObserver implements Observer{
public void update(double money){
//發送郵件通知賬號資金變動
System.out.println("郵件提醒:您的賬戶資金變動:" + money);
}
}
//簡訊觀察者
public class SmsObserver implements Observer{
public void update(double money){
//發送簡訊通知賬號資金變動
System.out.println("簡訊提醒:您的賬戶資金變動:" + money);
}
}
//客戶銀行賬號主題
public class AccountSubject extends AbstractSubject{
//觀察者集合
private List<Observer> observers;
//資金變動
private double money;
public AccountSubject(){
//主題實例化時,初始化觀察者集合
observers = new ArrayList<Observer>();
}
//賬號資金變動時通知觀察者
public void notifyObservers() {
if(observers.size() > 0){
for(Observer observer:observers){
observer.update(money);
}
}
}
//註冊觀察者
public void registerObserver(Observer o) {
observers.add(o);
}
//刪除觀察者
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i > 0){
observers.remove(i);
}
}
//改變賬戶資金
public void changeAccount(double money){
System.out.println("賬戶資金變動:" + money);
this.money = money;
//通知觀察者
notifyObservers();
}
}
//測試觀察者
public class TestObserver {
public static void main(String[] args){
//創建賬號主題
AccountSubject subject = new AccountSubject();
//創建觀察者
Observer appObserver = new APPObserver();
Observer smsObserver = new SmsObserver();
Observer mailObserver = new MailObserver();
//註冊觀察者到賬號主題
subject.registerObserver(appObserver);
subject.registerObserver(mailObserver);
subject.registerObserver(smsObserver);
subject.changeAccount(7.8);
}
}
測試結果:
其實 Java內置了觀察者模式,我們可以直接使用java.util包中的Obserber介面和Observable類實現觀察者模式。下麵我們把上面的代碼稍微修改,用Java內置的觀察者模式實現銀行賬號資金變動下發各種通知。
//首先我們創建被觀察者
import java.util.Observable;
public class AccountSubject extends Observable{
//資金變動數額
private double money;
public AccountSubject(){
}
//改變賬戶資金
public void changeAccount(double money){
System.out.println("(測試java自帶觀察者模式)賬戶資金變動:" + money);
this.money = money;
//被觀察者狀態改變
this.setChanged();
//java內部實現推拉通知,使用時直接調用notifyObservers(),
//或者notifyObservers(Object o),傳遞的參數會在觀察者類接收到
notifyObservers();
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
//app客戶端觀察者
import java.util.Observable;
import java.util.Observer;
public class APPObserver implements Observer{
//在觀察者類圖中我們看到,觀察者可以持用被觀察者的引用
Observable observale;
public APPObserver(Observable observable){
this.observale = observable;
//直接調用java自帶的方法,把觀察者註冊到被觀察者
observable.addObserver(this);
}
//具體觀察者實現Observer介面的方法,
//arg1參數就是被觀察者的notifyObservers(Object o)傳過來的參數
public void update(Observable arg0, Object arg1) {
if(arg0 instanceof AccountSubject){
AccountSubject accountSubject = (AccountSubject)arg0;
//發送app客戶端通知賬號資金變動
System.out.println("app客戶端提醒:您的賬戶資金變動:" + accountSubject.getMoney());
}
}
}
//郵件觀察者
import java.util.Observable;
import java.util.Observer;
public class MailObserver implements Observer{
//在觀察者類圖中我們看到,觀察者可以持用被觀察者的引用
Observable observale;
public MailObserver(Observable observable){
this.observale = observable;
//直接調用java自帶的方法,把觀察者註冊到被觀察者
observable.addObserver(this);
}
//java的觀察者有自帶的update方法
public void update(Observable arg0, Object arg1) {
if(arg0 instanceof AccountSubject){
AccountSubject accountSubject = (AccountSubject)arg0;
//發送app客戶端通知賬號資金變動
System.out.println("郵件提醒:您的賬戶資金變動:" + + accountSubject.getMoney());
}
}
}
//簡訊觀察者
import java.util.Observable;
import java.util.Observer;
public class SmsObserver implements Observer{
//在觀察者類圖中我們看到,觀察者可以持用被觀察者的引用
Observable observale;
public SmsObserver(Observable observable){
this.observale = observable;
//直接調用java自帶的方法,把觀察者註冊到被觀察者
observable.addObserver(this);
}
//java的觀察者有自帶的update方法
public void update(Observable arg0, Object arg1) {
if(arg0 instanceof AccountSubject){
AccountSubject accountSubject = (AccountSubject)arg0;
//發送app客戶端通知賬號資金變動
System.out.println("簡訊提醒:您的賬戶資金變動:" + + accountSubject.getMoney());
}
}
}
//測試java自帶觀察者模式
import java.util.Observer;
public class TestObserver {
public static void main(String[] args){
//創建被觀察者,也就是賬號主題
AccountSubject observble = new AccountSubject();
//創建觀察者
Observer appObserver = new APPObserver(observble);
Observer smsObserver = new SmsObserver(observble);
Observer mailObserver = new MailObserver(observble);
//賬號資金變動
observble.changeAccount(7.8);
}
}
測試結果:
六、優點與缺點
1.優點
(1)、觀察者模式解除了主題和具體觀察者的耦合,讓耦合的雙方都依賴抽象,而不是依賴具體。從而使各自的變化都不會影響到另一方。主題並不知道任何一個具體的觀察者,只知道他們有一個共同的介面。
(2)、觀察者模式支持“廣播通信”。主題會向所有的觀察者發出通知。
(3)、觀察者模式符合“對修改關閉,對擴展開放”的設計原則。
2.缺點
(1)、如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會話費很多時間。
(2)、觀察者只知道被觀察者發生了變化,但並不知道被觀察者是如何發生變化的。
七、使用場景
1、一個抽象模型有兩個方面,其中一個方面依賴另一個方面。將這些方面封裝在獨立的對象中使它們可以各自獨立的改變和復用。
2、一個對象的改變將導致其他一個或多個對象也發生改變,而不知道具體有多少個對象將發生改變,可以降低對象之間的耦合度。
3、一個對象必須通知其他對象,但而不知道這些對象是誰。就是對象之間的松耦合
八、總結
1、觀察者模式定義了對象之間一對多關係。
2、有多個觀察者時,不可以依賴特定的通知次序。
3、java有通用的觀察者模式(java.util.Observable)。
4、觀察者與被觀察者之間用松耦合方式結合。
5、簡單理解就是一個對象狀態的改變要通知到其它對象。