1. 程式人生 > >Java設計模式學習記錄-觀察者模式

Java設計模式學習記錄-觀察者模式

前言

觀察者模式也是物件行為模式的一種,又叫做發表-訂閱(Publish/Subscribe)模式、模型-檢視(Model/View)模式、 咱們目前用的最多的就是各種MQ(Message Queue)都是基於這個模式的思想來實現的,生產者產生資料放到一個佇列中,消費者觀察生產者的訊息佇列的變化,從而接收訊息,執行消費者本身的邏輯。

觀察者模式

概念介紹

觀察者模式定義了一個一對多的依賴關係,讓一個或多個觀察者物件監察一個主題物件。這樣一個主題物件在狀態上的變化能夠通知所有的依賴於此物件的那些觀察者物件,使這些觀察者物件能夠自動更新。

這些觀察者之間沒有任何關聯,可以根據業務需要增加刪除觀察者,易於系統擴充套件。

舉例

還是來舉實際的例子,來介紹設計模式,畢竟設計模式是一種抽象的東西,需要落到真正的實現中才能體現出它的價值。當我們在網上購物時,看到一件自己比較喜歡的商品,但是最近手頭有點緊(已經開始吃土了),所以會先關注一下這個商品,一般的購物網站上都會有關注此商品這麼一個功能的。為了就是當商品降價打折或是其他變化的時候能夠通知到所有關注此商品的顧客。那麼我們就以這個功能為例子來使用觀察者模式實現一下。

抽象主題類

/**
 * 抽象被觀察類
 */
@Getter
public abstract class Observable {

    //觀察者集合,儲存關注商品的所有顧客
    protected
List<Observer> observerList = Lists.newArrayList(); /** * 新增觀察者(當一個顧客選擇了關注商品時新增到觀察者集合中) * @param observer 觀察者 */ public void attach(Observer observer){ observerList.add(observer); } /** * 登出觀察者(取消關注商品) * @param observer 觀察者 */ public
void detach(Observer observer){ observerList.remove(observer); } /** * 通知觀察者的方法 */ public abstract void notice(Object obj); }

商品類

/**
 * 商品類
 */
@Getter //lombok get方法
@AllArgsConstructor //lombok 以所有屬性為引數的構造方法
@NoArgsConstructor //lombok 沒有引數的構造方法
public class Product extends Observable {

    /** 商品名稱 */
    protected String name;
    /** 商品價格*/
    protected BigDecimal price;

    /**
     * 商品名稱變更
     * @param name 商品名稱
     */
    public void setName(String name){
        this.name = name;
        //通知觀察者
        notice(name);
    }

    /**
     * 價格變更
     * @param price 商品價格
     */
    public void setPrice(BigDecimal price){
        this.price = price;
        //通知觀察者
        notice(price);
    }

    /**
     * 通知觀察者的方法
     */
    @Override
    public void notice(Object obj) {
        if(Objects.nonNull(observerList)&&observerList.size()>0){
            observerList.forEach((Observer observer) -> observer.update(obj));
        }
    }
}

抽象觀察者類

/**
 * 抽象觀察者
 */
public abstract class Observer {
    /**
     * 更新
     * @param obj 更新物件
     */
    public abstract void update(Object obj);

}

名稱觀察者

/**
 * 名稱觀察者
 */
public class NameObserver extends Observer {
    /**
     * 更新
     * @param obj 更新物件
     */
    @Override
    public void update(Object obj) {
        if(obj instanceof String){
            String name = (String) obj;
            System.out.println("您關注的商品名稱發生了變化,最新的商品名稱是"+name);
        }
    }
}

價格觀察者

/**
 * 價格觀察者 
 */
public class PriceObserver extends Observer{


    /**
     * 更新
     *
     * @param obj 更新物件
     */
    @Override
    public void update(Object obj) {
        if(obj instanceof BigDecimal){
            BigDecimal price = (BigDecimal)obj;
            System.out.println("您關注的商品價格發生了變化,最新的商品價格是:"+price);
        }
    }
}

測試類

public class Test {

    public static void main(String[] args) {

        Product product = new Product("iphoneX",new BigDecimal(8999));
        System.out.println("您關注的商品的名稱是:"+product.getName()+",價格是:"+product.getPrice());
        //建立觀察者
        NameObserver nameObserver = new NameObserver();
        PriceObserver priceObserver = new PriceObserver();
        //加入觀察者
        product.attach(nameObserver);
        product.attach(priceObserver);
        //產生變化,通知觀察者
        product.setName("iphoneX Max");
        product.setPrice(new BigDecimal(12999));
    }

}

執行結果:

您關注的商品的名稱是:iphoneX,價格是:8999
您關注的商品名稱發生了變化,最新的商品名稱是iphoneX Max
您關注的商品價格發生了變化,最新的商品價格是:12999

通過上面的執行結果我們就能看出來,當商品名稱或價格發生變化時,會通知到相應的觀察者,這就是觀察者模式的具體應用了。那麼通過例子我們也可以看出來觀察者模式具體是由哪些角色組成的。

觀察者模式的結構

觀察者模式結構如下圖 

在觀察者模式中存在如下幾種角色:

抽象主題角色(Subject):抽象主題角色把所有的觀察者物件的引用儲存在一個列表裡;每個主題都可以有任何數量的觀察者。主題提供一個介面,可以加上或撤銷觀察者物件;主題角色又被稱為被觀察者角色。可以用抽象類或介面來實現。

抽象觀察者角色(Observer):為所有的具體觀察者定義一個介面,在得到通知時更新自己。抽象觀察者角色通常是用一個抽象類或一個介面來實現;當然也可以用具體的類來實現。

具體主題角色(ConcreteSubject):具體主題儲存對具體觀察者物件有用的內部狀態,在這種狀態改變時,給其觀察者發出一個具體的通知,具體主題角色又被稱為具體被觀察者角色。

具體觀察者角色(ConcreteObserver):具體觀察者角色用於儲存一個指向具體主題物件的引用,和一個與主題的狀態相符的狀態。具體觀察者角色實現抽象觀察者角色所要求的更新自己的介面,以便使本身的狀態與主題的狀態對應。

總結

觀察者模式是一種使用頻率比較高的設計模式,凡是涉及到一對一或一對多的物件互動場景都可以使用觀察者模式。

觀察者模式的主要優點

1、觀察者模式可以實現表示層和資料邏輯層的分離,定義了穩定的訊息更新傳遞機制,並抽象了更新介面,使得可以有各種各樣不同的表示層充當觀察者角色。

2、觀察者模式在觀察目標和觀察者之間建立一個抽象耦合。觀察目標只需要維持一個抽象觀察者的集合,無須瞭解其具體觀察者。由於觀察目標和觀察者沒有緊密地耦合在一起,因此它們可以屬於不同的抽象化層次。

3、觀察者模式支援廣播通訊,觀察目標會向所有已註冊的觀察者物件傳送通知,簡化了一對多系統設計的難度。

4、觀察者模式滿足“開閉原則”的要求,增加新的具體觀察者無須修改原有系統程式碼,在具體觀察者與觀察目標之間不存在關聯關係的情況下,增加新的觀察目標也很方便。

觀察者模式的主要缺點

1、如果一個觀察目標物件有很多直接和間接觀察者,將所有的觀察者都通知到會花費很多時間。

2、如果在觀察者和觀察目標之間存在迴圈依賴,觀察目標會觸發它們之間進行迴圈呼叫,可能導致系統崩潰。

3、觀察者模式沒有相應的機制讓觀察者知道所觀察的目標物件是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。

適用場景

1、一個物件的改變將會導致一個或多個物件的改變,不清楚具體有多少物件以及這些被影響的物件是誰的情況。

2、如果有這樣一個影響鏈的情況下也可以使用,例如A的改變會影響B,B的改變會影響C......,可以使用觀察者模式設計一個鏈式觸發機制。

 

這個是我的個人公眾號,文章以後也會同步到公眾號上去,歡迎關注。