設計模式 #6 (觀察者模式)

凌丹妙耀發表於2020-09-21

設計模式 #6 (觀察者模式)


文章中所有工程程式碼和UML建模檔案都在我的這個GitHub的公開庫--->DesignPatternStar來一個好嗎?秋梨膏!


觀察者模式

簡述:被觀察者發生變化時,所有被觀察者都會通知。

需求:要求報紙釋出的時候,讀者可以及時收到報紙的最新報導。

根據之前多次的反例經驗,要做好面向介面和抽象程式設計的設計。

這次直接上,

正例 #1:

為觀察者定義一個介面。

//觀察者介面
 interface  Observer {
     void update(String Data);

     void disObservered(TheSubject subject);

     void doObservered(TheSubject subject);
}

這裡有一個問題:為什麼不用抽象類進行抽象?

因為不知道觀察者模式的應用都是怎麼樣的。現實程式設計中,具體的觀察者完全有可能是風馬牛不相及的類,但它們都需要根據通知者的通知來做出update()的操作,所以讓它們都實現Observer介面,並重寫相關方法即可。

定義一個觀察者:

public class Reader implements Observer{
    private String name ;

    public Reader(String name) {
        this.name = name;
    }

    @Override
    public void update(String Data) {
        System.out.println(this.name+"收到報紙,閱讀最新報導:"+Data);
    }

    @Override
    public void disObservered(TheSubject subject) {
        subject.removeObserver(this);
    }

    @Override
    public void doObservered(TheSubject subject) {
        subject.addObserver(this);
    }
}
//被觀察者抽象類
public abstract class TheSubject {
    //觀察者列表
    protected List<Observer> observerList = new ArrayList<>();
    //加入到觀察者列表
    public  boolean addObserver(Observer observer){
        return  observerList.add(observer);
    }
    //從觀察者列表移除
    public  boolean removeObserver(Observer observer){
        return observerList.remove(observer);
    }
    //變化,並通知觀察者
    public abstract void setChange(String Data);
    //為觀察者更新內容
    public abstract void upDataObserver(String Data);
}

唉?那這裡為什麼不可以用介面呢?

注意到有一個變數observerList了嗎?

我們先假設TheSubject抽象類是一個介面,如果是需要一個類成為被觀察者,那麼就會讓他實現這一介面,也是需要定義它的觀察者列表observerList,如果是每一個被觀察者都需要重新定義自己的觀察者列表observerList,還要在實現addObserverremoveObserver兩個方法,這兩個方法都是固定的演算法。重複太多的程式碼就會有壞的味道的。

這時候,將重複的程式碼寫在抽象類TheSubject,被觀察者進行繼承即可。

public class News_pappers extends TheSubject{
    private  String news;

    public String getNews() {
        return news;
    }

    @Override
    public void setChange(String Data) {
        this.news ="最新報導:"+ Data;
        System.out.println("釋出最新報導:"+Data);
        upDataObserver(Date);
    }

    @Override
    public void upDataObserver(String Data) {
        for (Observer observer : observerList) {
            observer.update(Data);
        }
    }
}
public class postive {
/*================客戶端============================*/
    public static void main(String[] args) {
        News_pappers newspaper = new News_pappers();
        Reader lili = new Reader("lili");
        Reader mumu = new Reader("mumu");
        Reader shanshan = new Reader("shanshan");

        newspaper.addObserver(lili);
        newspaper.addObserver(mumu);
        newspaper.addObserver(shanshan);

        newspaper.setChange("Big News");

        shanshan.disObservered(newspaper);

        newspaper.setChange("small00000 News");

        News_pappers news_pappers_02 = new News_pappers();
        mumu.doObservered(news_pappers_02);
        news_pappers_02.setChange("大新聞");

    }
}

執行結果:

image-20200921201557214

UML類圖:

image-20200921204803554

細心的讀者可能已經發現,這裡遵守的設計原則有:

  • 增加其他型別觀察者時,只需要Observer介面即可;增加被觀察者繼承抽象類TheSubject即可,符合開閉原則
  • 介面Observer和抽象類TheSubject相互依賴,並不涉及相關具體的觀察者和被觀察者,符合依賴倒置原則

其實接觸過安卓都知道,安卓的內容提供者和內容觀察者,廣播機制都有點觀察者模式的味道。

最後總結觀察者模式:

  • 當一個物件的改變需要同時改變其他物件的時候,而且它不知道具體有多少物件有待改變時,應該考慮使用觀察者模式。
  • 當一個抽象模型有兩個方面,其中一方面依賴於另一方面,這時用觀察者模式可以將這兩者封裝在獨立的物件中使它們各自獨立地改變和複用
  • 觀察者模式所做的工作其實就是在解除耦合。讓耦合的雙方都依賴於抽象,而不是依賴於具體。從而使得各自的變化都不會影響另一邊的變化。
  • 觀察者和被觀察者可以是多對多的關係。
  • 觀察者抽象成介面,被觀察者抽象於抽象類比較合適。

這是最後一篇關於設計模式的相關隨筆,回顧以往設計模式相關原創隨筆請點選---> 設計原則和常用設計模式

相關文章