設計模式 | 觀察者模式/發佈-訂閱模式(observer/publish-subscribe)

定義:

定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知全部觀察者對象,使他們可以自動更新本身。java

結構:(書中圖,侵刪)

一個抽象的觀察者接口,擁有一個更新方法
若干個具體的觀察者類
一個抽象的subject類,包含一個抽象觀察者的集合,並擁有增長、刪除觀察者的方法,以及一個通知的方法
若干個具體subject類

實例:

舉個看球賽的例子吧。
假設比賽在主場比。
subject就是球賽賽況;具體的subject分爲現場看、電視上看。
而後觀察者分爲主場球迷,客場球迷。
 
抽象觀察者接口(球迷接口):
package designpattern.observer;

public interface Fans {
    public void react(String result);
}
具體球迷類:
主場球迷:
package designpattern.observer;

public class HomeFans implements Fans {

    @Override
    public void react(String result) {
        System.out.print("主場球迷:");
        if (result.equals("win")) {
            System.out.println("Yes! 咱們贏啦,主場就是牛批!!!");
        } else {
            System.out.println("輸了,哎...");
        }
    }

}
客場球迷:
package designpattern.observer;

public class AwayFans implements Fans {

    @Override
    public void react(String result) {
        System.out.print("客場球迷:");
        if (result.equals("win")) {
            System.out.println("輸了,哎...");
        } else {
            System.out.println("Yes! 客場都贏啦,牛批!!!");
        }
    }

}
抽象subject接口(比賽接口):
package designpattern.observer;

import java.util.ArrayList;
import java.util.List;

public interface Match {
    List<Fans> fansList = new ArrayList<>();// 這一句

    void addFans(Fans fans);

    void removeFans(Fans fans);

    void notifyResult(String result);

}
註釋的那一句說明一下:
書中這句就是寫在接口裏的,可是寫在這裏就是一個全局的,全部子類都共用這同一個list
但我本身感受各個子類所須要通知的觀察者應該是不必定同樣的,因此我是改到了子類中
但我後來寫總結的時候回看定義,發現,總結寫的就是「一對多的依賴關係」,既然不是多對多,那天然觀察者就是統一調配的
只不過仍是感受有點怪怪的,往集合裏添加刪除觀察者的方法是否是改爲靜態方法會好一點?
否則,必需要實例化一個子類的對象才能調用。
 
現場看比賽:
package designpattern.observer;

import java.util.ArrayList;
import java.util.List;

public class MatchOnTheSpot implements Match {
    // List<Fans> fansList = new ArrayList<>();

    @Override
    public void addFans(Fans fans) {
        fansList.add(fans);
    }

    @Override
    public void removeFans(Fans fans) {
        fansList.remove(fans);
    }

    @Override
    public void notifyResult(String result) {
        for (Fans fans : fansList) {
            fans.react(result);
        }
    }
}
電視看比賽:
package designpattern.observer;

import java.util.ArrayList;
import java.util.List;

public class MatchOnTheTV implements Match {
    // List<Fans> fansList = new ArrayList<>();

    @Override
    public void addFans(Fans fans) {
        fansList.add(fans);
    }

    @Override
    public void removeFans(Fans fans) {
        fansList.remove(fans);
    }

    @Override
    public void notifyResult(String result) {
        for (Fans fans : fansList) {
            fans.react(result);
        }
    }
}
此例中,兩個子類的代碼徹底相同,能夠把接口改爲抽象類,而後把方法寫到父類中,我這裏就不改了。
 
客戶端:
package designpattern.observer;

public class Client {
    public static void main(String[] args) {
        HomeFans homeFans = new HomeFans();
        AwayFans awayFans = new AwayFans();

        // 現場看
        Match match = new MatchOnTheSpot();
        match.addFans(homeFans);
        match.addFans(awayFans);
        System.out.println("主場贏啦~");
        match.notifyResult("win");

        System.out.println("===========================");

        // 電視上看
        match = new MatchOnTheTV();
        System.out.println("主場輸啦~");
        match.notifyResult("lose");

    }
}
結果輸出:
主場贏啦~
主場球迷:Yes! 咱們贏啦,主場就是牛批!!!
客場球迷:輸了,哎...
===========================
主場輸啦~
主場球迷:輸了,哎...
客場球迷:Yes! 客場都贏啦,牛批!!!

總結:

(按理說總結纔是精華,但我每次總結都不知道要寫點什麼,多是理解得還不夠深入吧。)
這個設計模式能夠在一個對象改變的同時須要其餘對象也跟着改變的時候使用。
好處說來講去就是那麼幾點,解除了觀察者和通知者之間的耦合,將依賴轉移給抽象,符合依賴倒轉原則。
缺點嘛,就是這裏的通知調用的都是同一方法(本例中就是notifyResult()方法),而實際應用中,多是各類各樣的方法名,還有可能有一些是別人已經封裝好的方法,根本不可能去繼承你的抽象觀察者接口。
書中使用了一個叫事件委託的技術來解決這個問題,而後java中並無直接的封裝,實現這個須要用到反射,稍微有點麻煩,我這裏就先不實現了,等之後再回來補全。有興趣的能夠本身去試一下。
相關文章
相關標籤/搜索