小明和小劉十分喜歡一款名爲《電腦愛好者》的雜誌,就像公衆號的推送同樣,他們想獲得這款雜誌的出版消息,而後去書店購買。java
當前雜誌設計模式
/** * 當前雜誌/最新雜誌 */ public class CurrentMagazine { private String name; // 當前雜誌名稱 public String getName() { return name; } public void setName(String name) { this.name = name; this.inform(); // 雜誌更新時調用inform方法通知小明和小劉 } private void inform() { XiaoMing.update(this); XiaoLiu.update(this); } }
小明與小劉實現相關update
方法,當雜誌更新時通知小明和小劉。ide
/** * 小明 */ public class XiaoMing { public static void update(CurrentMagazine magazine) { System.out.println("小明:" + magazine.getName() + "出版了,我要去書店購買。"); } } /** * 小劉 */ public class XiaoLiu { public static void update(CurrentMagazine magazine) { System.out.println("小劉:" + magazine.getName() + "出版了,我要去書店購買。"); } }
main
方法:優化
public class Main { public static void main(String[] args) { CurrentMagazine currentMagazine = new CurrentMagazine(); currentMagazine.setName("電腦愛好者2018年第10期"); currentMagazine.setName("電腦愛好者2018年第11期"); } }
運行結果:this
爲何說這個實現不太優雅,這種實現是在inform
中通知小明和小劉更新雜誌的,可是這是常常變化的,可能又來幾我的想訂購雜誌。咱們要改動的地方有兩處。spa
爲該人員實現update
方法,同時在inform
中通知該人員。改動的地方多了,天然可能被遺忘,咱們須要一個一勞永逸的方案。設計
inform
中的代碼是高度重複的,都是調用update
並傳this
進去,而且這個訂購雜誌的人是常常變更的。因此咱們須要創建一個訂閱該雜誌的一些人的集合。code
List readers = new ArrayList();
而後咱們在inform
中就不用一行一行的寫我要通知誰,直接遍歷這個列表,都去調用其中的update
。orm
相信你已經發現了問題,由於這裏沒有泛型,因此從List
中取出來的對象都是Object
類型,咱們怎麼能保證其必定有update
方法呢?怎麼能保證這些update
的參數都相同呢?因此咱們須要一個聲明update
的接口。server
/** * 接口:可更新 */ public interface Updatable { void update(Magazine magazine) } List<Updatable> readers = new ArrayList<Updatable>(); // 訂購雜誌的人員列表 /** * 優化後的inform方法 */ public void inform() { for (Updatable reader : readers) { reader.update(this); } }
優化以後,咱們進行訂閱變動時,無需修改inform
方法,咱們只須要去維護訂購該雜誌的人員列表就好了,減少了響應需求變動的成本。
若是你能理解我上面說的是什麼,那麼恭喜你。你已經明白了觀察者模式。
都說觀察者模式是在JDK
實現中使用的最多的設計模式,這裏姑且相信,畢竟,我也沒有閱讀過JDK
的源碼。
JDK
中已經爲咱們提供了觀察者模式的基礎,一個可供訂閱的類、一個描述交互消息的接口,這就是觀察者模式。
JDK
中可供訂閱的基類:
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) { 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 void deleteObservers() { obs.removeAllElements(); } protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); } }
JDK
中描述交互消息的接口:
package java.util; public interface Observer { void update(Observable o, Object arg); }
用於被訂閱的雜誌:
import java.util.Observable; /** * 當前雜誌/最新雜誌 */ public class CurrentMagazine extends Observable { private String name; // 當前雜誌名稱 public void setName(String name) { this.name = name; /** * 這裏設置改動的緣由是在notifyObservers中 * 判斷了是否有改動,若是改動爲false,則終止 */ this.setChanged(); // 設置有變化 this.notifyObservers(); // 通知觀察者 } public String getName() { return name; } }
觀察者,小明與小劉:
import java.util.Observable; import java.util.Observer; /** * 小明 */ public class XiaoMing implements Observer { @Override public void update(Observable observable, Object arg) { if (observable instanceof CurrentMagazine) { CurrentMagazine currentMagazine = (CurrentMagazine) observable; System.out.println("小明:" + currentMagazine.getName() + "出版了,我要去書店購買。"); } } } import java.util.Observable; import java.util.Observer; /** * 小劉 */ public class XiaoLiu implements Observer { @Override public void update(Observable observable, Object arg) { if (observable instanceof CurrentMagazine) { CurrentMagazine currentMagazine = (CurrentMagazine) observable; System.out.println("小劉:" + currentMagazine.getName() + "出版了,我要去書店購買。"); } } }
思考一下爲何這裏須要判斷當前Observable
的類型呢?
小明和小劉能夠訂購多個消息,能夠訂購雜誌,也能夠訂購天氣預報。可是二者調用的都是update
方法,因此要用instanceof
判斷到底是我訂閱的哪一個服務推送的消息,而後再進行相應處理。
main
方法:
import java.util.Observable; import java.util.Observer; public class Main { public static void main(String[] args) { Observable currentMagazine = new CurrentMagazine(); // 聲明被訂閱的雜誌 Observer xiaoMing = new XiaoMing(); // 聲明觀察者 Observer xiaoLiu = new XiaoLiu(); currentMagazine.addObserver(xiaoMing); // 觀察者訂閱雜誌 currentMagazine.addObserver(xiaoLiu); ((CurrentMagazine) currentMagazine).setName("電腦愛好者2018年第10期"); ((CurrentMagazine) currentMagazine).setName("電腦愛好者2018年第11期"); } }
聲明雜誌,聲明觀察者,讓觀察者訂閱雜誌,而後修改雜誌信息,觀察者會受到消息通知。
運行結果:
還記得代碼整潔之道中有一條規範,就是咱們的方法參數個數越少越好。參數越多,出錯的可能性就會越大。
這是Head First
設計模式中的一處代碼片斷。第一眼看到這段代碼,由於不符合個人風格嘛,天然多思考一下。
若是讓我去實現這個display
,我會爲其設置參數。
public void display(float tempature, float humidity) { System.out.println("Current Conditions: " + tempature + "F degrees and " + humidity + "% humidity"); }
對比之下,仍是感受書上的實現好。在類的內部,消息溝通天然隨意一些,直接使用內部變量便可溝通。而那些方法中的參數,都是本類不能本身提供,須要從外界獲取的內容。如此,就減小了參數的個數,下降了交流的成本。
類內部,使用變量進行溝通,畢竟,封裝的本質,就是減小各個操做之間數據的交流成本。之後多注意。