1. 程式人生 > >23種設計模式之---觀察者模式(Observer Factory)

23種設計模式之---觀察者模式(Observer Factory)

1.觀察者模式(行為型模式)

觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態發生變化時,會通知所有的觀察者物件,使它們能夠自己更新自己。

2.觀察者模式結構圖

3.觀察者模式結構圖分析

Subject類,可理解為主題或抽象通知者,一般用一個抽象類或一個介面實現。它把所有對觀察者物件的引用儲存在一個聚集中,每個主題都可以有任何數量的觀察者。抽象主題提供一個介面,可以增加和刪除觀察者物件。

public abstract class Subject{
    private List<Observer> observers = new ArrayList<Observer>();
    //增加觀察者
    public void attach(Observer observer){
        observers.add(observer);
    }
    //移除觀察者
    public void detach(Observer observer){
        observers.remove(observer);
    }
    //通知
    public void notify(){
        for(Observer observer : observers){
            observer .update();
        }
    }
}

Observer類,抽象的觀察者,為所有的具體觀察者定義一個介面,在得到主題的通知時更新自己.這個介面叫做更新介面。抽象觀察者一般用一個抽象類或者一個介面實現。更新介面通常包含一個update()方法,這個方法叫做更新方法。

public abstract class Observer{
    public abstract void update();
}

ConcreteSubject類,叫做具體主題或具體通知者,將有關狀態存入具體的觀察者物件;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色通常用一個具體的子類實現。

public class ConcreteSubject extends Subject{
    private String subjectState;
    //具體被觀察者狀態
     public String getSubjectState() {
        return subjectState;
    }
    public void setSubjectState(String subjectState) {
        this.subjectState = subjectState;
    }
}

ConcreteObserver類,具體觀察者,實現抽象觀察者角色所要求的的更新介面,以便使本身的狀態與主題的狀態相協調。具體觀察者角色可以儲存一個指向具體主題物件的引用。具體觀察者角色通常用一個具體子類實現。

public class ConcreteObserver extends Observer{
    private String name;
    private String observerState;
    private ConcreteSubject subject;
    
    public ConcreteObserver(ConcreteSubject subject,String name){
        this.subject = subject;
        this.name = name;
    }
    @Override
    public void update(){
        observerState = subject.getSubjectState();
        System.out.println("觀察者"+name+"的新狀態是:"+observerState);
    }
}

客戶端程式碼:

public static void main(String[] args){
    ConcreteSubject subject = new ConcreteSubject();
    subject.attach(new ConcreteObserver(s,"X"));
    subject.attach(new ConcreteObserver(s,"Y"));
    subject.attach(new ConcreteObserver(s,"Y"));
    s.setSubjectState = "ABC";
    s.notify();                    
}

控制檯列印的結果:

觀察者X的新狀態是:ABC
觀察者Y的新狀態是:ABC
觀察者Z的新狀態是:ABC

4.觀察者模式的特點

觀察者模式的動機:將一個系統分割成一系列相互協作的類有一個很不好的副作用,那就是需要維護相關物件間的一致性。我們不希望為了維護一致性而使各類緊密耦合,這樣會給維護,擴充套件和重用都帶來不便。而觀察者模式的物件是主題Subject和觀察者Observer,一個Subject可以有任意數目的依賴它的Observer,一旦Subject的狀態發生了改變,所有Observer都可以得到通知。Subject發出通知時並不需要知道誰是它的觀察者。

當一個抽象模型有兩方面,其中一方面依賴於另一方面,這是用觀察者模式可以將這兩者封裝在獨立的物件中使它們各自獨立地改變和複用。觀察者模式所做的工作其實就是在解除耦合。讓耦合的雙方都依賴於抽象,而不是依賴具體。從而使得各自的變化都不會影響另一邊的變化。這其實就是依賴倒轉原則的最佳的體現。

5.觀察者模式的不足

儘管已經用了依賴倒轉原則,但是抽象通知者還是依賴了抽象觀察者,也就是說,萬一沒有了抽象觀察者這樣的介面,通知的功能就完不成了。

6.使用事件委託實現(程式碼就不貼了,可以先學習EventHandler,然後再程式碼實現)

簡單來說就是每個具體的觀察者中有各自不同的update()方法,通過事件委託然後將這些具體的方法掛鉤到通知者的"更新"上。

事件委託說明:

委託就是一種引用方法的型別。一旦為委託分配了方法,委託將與該方法具有完全相同的行為。委託方法的使用可以將其他任何方法一樣,具有引數和返回值。委託可以看做是對函式的抽象,是函式的'類',委託的例項將代表一個具體的函式。一個委託可以搭載多個方法,所有方法依次喚起,更重要的是它可以使得委託物件所搭載的方法並不需要屬於同一個類。本來需要在具體的通知者(ConcreteSubject)類中的增加和減少的抽象觀察者集合以及通知時遍歷的抽象觀察者都不必要了。轉到客戶端來讓委託搭載多個方法,這就解決了本來與抽象觀察者的耦合問題。但委託也是有前提的,那就是委託物件所搭載的所有方法必須具有相同的原形和形式,也就是具有相同的引數列表和返回值型別。

注意:是先有的觀察者模式,再有的委託事件技術,它們各有優缺點。

在java中事件委託的實現是使用Event和EventHandler,具體Event和EventHandler是如何使用可以自己學習下。

7.總結

觀察者模式:很好的解耦了程式碼,體現了依賴倒轉原則;但抽象通知者還是依賴了抽象觀察者,當沒有觀察者時,沒辦法更新,而且要求觀察者的所有動作必須一樣,如果不一樣的話則不能實現。

時間委託機制:實現完全解耦;一次通知執行了不同類的不同方法;擴充套件性很高,重用性很好。

學習觀察者模式可以參考下這篇博文(包含了事件委託機制的程式碼實現): https://blog.csdn.net/gdutxiaoxu/article/details/51824769

學習了那麼久的設計模式,感覺更重要的在學習那些設計原則。比如說:依賴倒轉原則,介面隔離,開閉原則等。

在學習設計模式的時候是參考的《大話設計模式》,不同的是用java語言實現的,基本上就是對這本書的整理和總結;之所以不像其他人那樣改造成自己的語言來描述,一方面是覺得自己的語言不嚴謹,另一方面是覺得設計模式重在學習設計模式的思想,這些部落格更多的是為了給自己看的,所以能夠理解加記憶就行。