[設計模式]-觀察者模式

介紹(以下部分內容來自維基百科)

定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。

這話聽着很拗口,其實就是多個對象依賴於一個對象,並按照他的狀態變化改變自身.

結構

主要有四個類:

  1. 抽象目標類(接口).
  2. 具體的目標類.
  3. 抽象的觀察者類.
  4. 具體的觀察者類.

具體場景及代碼演示

你現在是一枚老韭菜!還是會寫代碼的韭菜!(簡單易割還長得快).

你每天都炒股,只盯着兩隻股票,一個是阿里巴巴,一個是騰訊.你想在任何你能接觸到的終端上面隨時的看到最新的股市消息.

因此你開發了下面的代碼(僞代碼):

if 阿里巴巴 有變化
  send (newest info) to 手機
  send (newest info) to 電腦
  send (newest info) to 手錶

這樣子沒啥問題,但是以後想要修改的時候,比如你明天新買了個pad(韭菜哪有錢…),那你就需要修改上述的代碼.

這樣不是很符合開閉原則,因此使用觀察者模式將他重構一下.

由於jdk在util包裏有對抽象目標抽象觀察者的定義,這裏不做重複的定義,學習一下這兩個接口.

抽象目標(被觀察者)



package java.util;

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }

    //添加觀察者
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    //刪除觀察者
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }

    //通知觀察者
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {

            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    public synchronized int countObservers() {
        return obs.size();
    }
}

可以看出,他定義並實現了結構圖中的三個方法,添加觀察者,刪除觀察者,通知觀察者.存儲觀察者使用的Vector.保證對觀察者的添加及刪除操作線程安全.對Vector想了解的朋友可以移步Vector源碼閱讀.

抽象觀察者

public interface Observer {
    //更新自身
    void update(Observable o, Object arg);
}

Observer只定義了一個方法,就是更新自身.

好,那麼到了重頭戲了,我們通過自己的實現來重構上面的項目.

股票類(具體的某個被觀察者)

package com.huyan.demo.observer;

import java.util.Observable;
import java.util.Observer;

/**
 * created by huyanshi on 2018/12/28
 */
public class Stock extends Observable {

  private String name;

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

  @Override
  public synchronized void addObserver(Observer o) {
    super.addObserver(o);
  }

  @Override
  public synchronized void deleteObserver(Observer o) {
    super.deleteObserver(o);
  }

  @Override
  public void notifyObservers(Object arg) {
    super.notifyObservers(arg);
  }

  public void stockUp() {
    System.out.println("漲了漲了!牛逼!");
    super.setChanged();
  }

  @Override
  public String toString() {
    return "這裏是股票:" + name;
  }
}

我們定義並實現了Stock類,擁有屬性:名字.

以及幾個重寫的方法,其實完全調用了父類的方法,這裏只是寫出來方便看.

此外定義了一個股票上漲的方法,在這個方法裏面調用了父類的serChange方法,代表着此時,被觀察者發生了變化.

具體觀察者(PC和手機)

public class PC implements Observer {

  @Override
  public void update(Observable o, Object arg) {
    System.out.println("------------PC已經更新.");
    System.out.println(arg.toString());
  }
}
public class Phone implements Observer {

  @Override
  public void update(Observable o, Object arg) {
    System.out.println("------------Phone已經更新.");
    System.out.println(arg.toString());
  }
}

這裏定義了PC和手機,實現了唯一的update方法,在裏面更新自己信息並打印,標識自己被通知到了.

測試

代碼

@Test
public void test(){
  //定義一個股票
  Stock stock = new Stock("阿里巴巴");
  //給它添加觀察者
  stock.addObserver(new PC());
  stock.addObserver(new Phone());

  //這裏阿里巴巴股票大漲!
  stock.stockUp();
  //通知所有觀察者
  stock.notifyObservers("阿里巴巴大漲0.01%");

}

這裏我們定義了一個股票(被觀察者),然後給它添加了手機和PC(觀察者),之後股票上漲(被觀察者變化),看一下結果,觀察者是否收到消息並更新自身信息.

輸出結果

漲了漲了!牛逼!
------------Phone已經更新.
阿里巴巴大漲0.01%
------------PC已經更新.
阿里巴巴大漲0.01%

可以看到,這裏在被觀察者發生變化後,所有(兩個)的觀察者都受到了消息並且做出了反應.

如何擴展呢?

這樣重構完之後,擴展變得十分方便,比如我們新買了pad.只需要新建一個Pad類,然後在添加觀察者步驟,將其添加即可.

想換一隻股票觀察或者添加一直股票,只需要new一個股票的對象重複上述操作即可.

總結

對象觀察一對多的情況下,觀察者模式成功的將我們的系統解耦,並提高了系統的可擴展性,但是有沒有問題呢?

有,如果這裏的終端不只是個人的,而是一羣人的,即:觀察者過多,一個個通知將會很慢.

因此,在發現場景適合後,仍需做一些調研,纔可以對代碼進行重寫,千萬不要得不償失.

完。





ChangeLog

2018-12-28 完成

以上皆爲個人所思所得,如有錯誤歡迎評論區指正。

歡迎轉載,煩請署名並保留原文鏈接。

聯繫郵箱:[email protected]

更多學習筆記見個人博客------>呼延十

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章