模式開發藝術之觀察者模式

何時使用觀察者模式

1、觸發聯動:當修改目標狀態時就會觸發相應的通知,然后會循環調用所有注冊的觀察者對象的相應方法。

2、建議在下面三種情況下使用觀察者模式:

1.當一個抽象模型有兩個方面,其中一個方面的操作依賴于另一個方面的狀態變化時

2.如果在封蓋一個對象的時候,需要同時連帶改變其他的對象,而且不知道究竟應該有對少對象需要被連帶改變

3.當一個對象必須通知其他的對象,但是你又希望這個對象和其他被他通知的對象是松散耦合的

實現方式一:觀察者模式 經典模版

觀察者模式的結構:Subject(被觀察者或者說是目標類):要有添加觀察者,刪除觀察者,和提醒觀察者(當被觀察者的狀態發生改變的時候調用這個方法)的方法,Observe(觀察者):要有更新方法(當觀察者狀態發生改變調用提醒方法后觀察者通過更新方法來做出不同響應(動作))

ConcreteSubject是Subject接口的實現類

ConcreteObserver是Observer接口的實現類

目標(被觀察者):

/**

* 目標對象,它知道觀察它的觀察者,并提供注冊(添加)和刪除觀察者的接口

*/

public class Subject {

? ? // 用來保存注冊的觀察者對象

? ? private List<Observer> observers = new ArrayList<Observer>();

? ? // attach detach notifyObservers

? ? // 把訂閱天氣的人添加到訂閱者列表中

? ? public void attach(Observer observer) {

? ? ? ? observers.add(observer);

? ? }

? ? /**

? ? * 刪除集合中的指定觀察者

? ? * @param observer

? ? */

? ? public void detach(Observer observer) {

? ? ? ? observers.remove(observer);

? ? }

? ? /**

? ? * 通知所有注冊的觀察者對象

? ? */

? ? protected void notifyObservers() {

? ? ? ? for (Observer observer : observers) {

? ? ? ? ? ? observer.update(this);

? ? ? ? }

? ? }

}

目標(被觀察者)具體實現:

/**

* 具體的目標對象,負責把有關狀態存入到相應的觀察者對象中

*/

public class ConcreteSubject extends Subject {

? ? // 目標對象的狀態

? ? private String Content;

? ? public String getSubjectState() {

? ? ? ? return Content;

? ? }

? ? public void setSubjectState(String Content) {

? ? ? ? this.Content = Content;

? ? ? ? // 內容有了,通知所有的訂閱的人

? ? ? ? this.notifyObservers();

? ? }

}

觀察者接口:

/**

* 這是一個觀察者接口,定義一個更新的接口給那些在目標發生改變的時候被通知的對象

*/

public interface Observer {

? ? /**

? ? * 更新的接口 傳入目標對象,方便獲取相應的目標對象的狀態

? ? * @param subject

? ? */

? ? void update(Subject subject);

}

觀察者接口的具體實現:

/**

* 具體的觀察者對象,實現更新的方法,使自身的狀態和目標的狀態保持一致

*/

public class ConcreteObserver implements Observer {

? ? // 觀察者的名字,是誰收到了這個訊息

? ? private String observerName;

? ? // 觀察者的狀態,這個消息從目標處獲取

? ? private String observerState;

? ? // 提醒的內容

? ? private String remindTing;

? ? /**

? ? * 獲取目標類的狀態同步到觀察者的狀態中

? ? */

? ? @Override

? ? public void update(Subject subject) {

? ? ? ? observerState = ((ConcreteSubject) subject).getSubjectState();

? ? ? ? System.out.println(observerName + "收到了, " + observerState + " , " + remindTing);

? ? }

? ? public String getObserverName() {

? ? ? ? return observerName;

? ? }

? ? public void setObserverName(String observerName) {

? ? ? ? this.observerName = observerName;

? ? }

? ? public String getRemindTing() {

? ? ? ? return remindTing;

? ? }

? ? public void setRemindTing(String remindTing) {

? ? ? ? this.remindTing = remindTing;

? ? }

}

測試類:

? ? public static void main(String[] args) {

? ? ? ? // 1.創建目標

? ? ? ? ConcreteSubject weather = new ConcreteSubject();

? ? ? ? // 2.創建觀察者

? ? ? ? ConcreteObserver observerGirl = new ConcreteObserver();

? ? ? ? observerGirl.setObserverName("小明的女朋友");

? ? ? ? observerGirl.setRemindTing("是我們的第一次約會,地點街心公園,不見不散哦");

? ? ? ? ConcreteObserver observerMum = new ConcreteObserver();

? ? ? ? observerMum.setObserverName("老媽");

? ? ? ? observerMum.setRemindTing("是一個購物的好日子,明天去天虹掃貨");

? ? ? ? // 3.注冊觀察者

? ? ? ? weather.attach(observerGirl);

? ? ? ? weather.attach(observerMum);

? ? ? ? // 4.目標發布天氣

? ? ? ? weather.setSubjectState("#明天天氣晴朗,藍天白云,氣溫28度#");

? ? }

實現方式二:利用Java提供的觀察者實現 觀察者模式

Java 實現 VS 自己實現的對比四點:

(1)不需要再定義觀察者和目標接口(JDK已經定義)。

(2)具體的目標實現里面不需要再維護觀察者的注冊信息,Java中的Observable類里面已經實現。

(3)觸發通知的方式有一點變化,要先調用setChanged方法,這個是Java為了幫助實現更精確的觸發控制而提供的功能。

(4)具體觀察者的實現里面,update方法其實能同時支持推模型和拉模型,這個Java在定義的時候,已經考慮。

目標(被觀察者):

/**

* 被觀察者的具體實現

*/

public class ConcreteSubject extends Observable {

? ? // 變化的具體內容

? ? private String content;

? ? public String getContent() {

? ? ? ? return content;

? ? }

? ? public void setContent(String content) {

? ? ? ? this.content = content;

? ? ? ? // 發生變化,就要通知所有的觀察者

? ? ? ? // 注意在通知之前,在用Java中的Observer模式的時候,下面這句話不可少

? ? ? ? this.setChanged();

? ? ? ? // 然后主動通知,這里我們先用推的方式

? ? ? ? this.notifyObservers(content);

? ? ? ? // 如果是拉的方式,我們就調用

? ? ? ? // this.notifyObservers();

? ? }

}

觀察者:

/**

* 具體的觀察者對象

*/

public class ConcreteObserver implements Observer{

? ? //觀察者名稱的變量

? ? private String observerName;

? ? @Override

? ? public void update(Observable o, Object arg) {

? ? ? ? //第一種是推的方式

? ? ? ? System.out.println(observerName+"收到了消息,目標推送過來的是"+arg);

? ? ? ? //第二種是拉的方式

? ? ? ? System.out.println(observerName+"收到了消息,主動到目標對象中去拉,拉的內容是"+((ConcreteSubject)o).getContent());

? ? }

? ? public String getObserverName() {

? ? ? ? return observerName;

? ? }

? ? public void setObserverName(String observerName) {

? ? ? ? this.observerName = observerName;

? ? }

}

測試類:

public static void main(String[] args) {

? ? ? ? // 創建一個目標,也就是被觀察者

? ? ? ? ConcreteSubject subject = new ConcreteSubject();

? ? ? ? // 創建小明的女朋友作為觀察者

? ? ? ? ConcreteObserver girl = new ConcreteObserver();

? ? ? ? girl.setObserverName("小明的女朋友");

? ? ? ? // 創建小明的老媽作為觀察者

? ? ? ? ConcreteObserver mum = new ConcreteObserver();

? ? ? ? mum.setObserverName("小明的老媽");

? ? ? ? // 注冊觀察者

? ? ? ? subject.addObserver(girl);

? ? ? ? subject.addObserver(mum);

? ? ? ? // 目標更新天氣情況了

? ? ? ? subject.setContent("天氣晴,氣溫28度");

? ? }

觀察者優缺點

1、觀察者模式的優點:

(1)觀察者模式實現了觀察者和目標之間的抽象耦合

(2)觀察者模式實現了動態聯動

(3)觀察者模式支持廣播通信

2、觀察者模式的缺點:可能會引起無謂的操作。

實現方式三:區別對待觀察者場景問題 (靈活定制觀察者)

區別觀察者模式是,目標父類不實現通知方法,在子類中實現有區別的通知方法。

區別對待的觀察者模型中和通用觀察者模型的區別在于:要根據不同的觀察者來進行不同的推送,所以區別在于目標類中的通知更新方法需要在具體的目標類中進行實現。(因為需要根據不同的情況進行更新,所以需要在具體的目標類中實現剛剛那個方法)

區別對待觀察者,邏輯判斷讓觀察者實現更符合邏輯,定義到接口觀察者中,setRule(),讓小明女朋友和老媽自己去定義

目標(被觀察者)的抽象方法:

public abstract class Subject {

? ? // 用來保存注冊的觀察者對象

? ? public List<Observer> observers = new ArrayList<Observer>();

? ? // attach detach abstract notifyObservers

? ? // 把觀察者添加到訂閱者列表中

? ? public void attach(Observer observer) {

? ? ? ? observers.add(observer);

? ? }

? ? // 刪除集合中指定的訂閱天氣的人

? ? public void detach(Observer observer) {

? ? ? ? observers.remove(observer);

? ? }

? ? protected abstract void notifyObservers();

}

目標(被觀察者)的具體實現:

public class ConcreteSubject extends Subject {

? ? // "晴天" "下雨" "下雪"

? ? // 目標對象的狀態

? ? private String Content;

? ? @Override

? ? protected void notifyObservers() {

? ? ? ? // 循環所有注冊的觀察者

? ? ? ? for (Observer observer : observers) {

? ? ? ? ? ? // 情況之一:

? ? ? ? ? ? // 如果天氣是晴天,按照小明的女朋友需要下雨的條件,小明的老媽需要下雨或下雪的條件,則她們倆就都不需要通知了。

? ? ? ? ? ? // 情況之二:

? ? ? ? ? ? // 如果天氣是下雨,則小明的女朋友需要通知,而小明的老媽也需要通知。

? ? ? ? ? ? // 情況之三:

? ? ? ? ? ? // 如果天氣是下雪,則只需要通知小明的老媽。

? ? ? ? ? ? if("下雨".equals(this.getContent())){

? ? ? ? ? ? ? ? if("小明的女朋友".equals(observer.getObserverName())){

? ? ? ? ? ? ? ? ? ? observer.update(this);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? if("小明的老媽".equals(observer.getObserverName())){

? ? ? ? ? ? ? ? ? ? observer.update(this);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? ? ? if("下雪".equals(this.getContent())){

? ? ? ? ? ? ? ? if("小明的老媽".equals(observer.getObserverName())){

? ? ? ? ? ? ? ? ? ? observer.update(this);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? public String getContent() {

? ? ? ? return Content;

? ? }

? ? public void setContent(String content) {

? ? ? ? Content = content;

? ? ? ? this.notifyObservers();

? ? }

}

觀察者的接口:

/**

* 定義一個更新的接口方法給那些在目標發生改變的時候被通知的觀察者對象調用

*/

public interface Observer {

? ? // 更新的接口

? ? public void update(Subject subject);

? ? // 設置觀察者名稱

? ? public void setObserverName(String observerName);

? ? // 取得觀察者名稱

? ? public String getObserverName();

}

觀察者的具體實現:

public class ConcreteObserver implements Observer {

? ? // 觀察者的名稱

? ? private String observerName;

? ? // 天氣情況的內容

? ? private String content;

? ? // 提醒的內容

? ? private String remindThing;

? ? @Override

? ? public void update(Subject subject) {

? ? ? ? content = ((ConcreteSubject) subject).getContent();

? ? ? ? System.out.println(observerName + "收到了<" + content + ">," + remindThing);

? ? }

? ? @Override

? ? public void setObserverName(String observerName) {

? ? ? ? this.observerName = observerName;

? ? }

? ? @Override

? ? public String getObserverName() {

? ? ? ? return observerName;

? ? }

? ? public String getContent() {

? ? ? ? return content;

? ? }

? ? public void setContent(String content) {

? ? ? ? this.content = content;

? ? }

? ? public String getRemindThing() {

? ? ? ? return remindThing;

? ? }

? ? public void setRemindThing(String remindThing) {

? ? ? ? this.remindThing = remindThing;

? ? }

}

測試類:

public static void main(String[] args) {

? ? ? ? // 1.創建目標

? ? ? ? ConcreteSubject weather = new ConcreteSubject();

? ? ? ? // 2.創建觀察者

? ? ? ? ConcreteObserver observerGirl = new ConcreteObserver();

? ? ? ? observerGirl.setObserverName("小明的女朋友");

? ? ? ? observerGirl.setRemindThing("下雨了,安靜的呆在家里吧");

? ? ? ? ConcreteObserver observerMum = new ConcreteObserver();

? ? ? ? observerMum.setObserverName("小明的老媽");

? ? ? ? observerMum.setRemindThing("不管下雨還是下雪,我都不出門了");

? ? ? ? // 3.注冊觀察者

? ? ? ? weather.attach(observerGirl);

? ? ? ? weather.attach(observerMum);

? ? ? ? // 4.目標發布天氣

? ? ? ? weather.setContent("下雪");

? ? }

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容

  • 觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通...
    扈扈哈嘿閱讀 1,338評論 0 12
  • 觀察者模式也叫作發布-訂閱模式,也就是事件監聽機制。觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽...
    超級大雞腿閱讀 236評論 0 0
  • 設計模式分類 總體來說設計模式分為三大類:創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原...
    lifeline丿毅閱讀 1,238評論 0 2
  • 觀察者模式是使用最為頻繁的設計模式之一。在很多地方都有用到。比如各種編程語言的GUI事件處理實現,各種框架的實現,...
    樹獺非懶閱讀 15,657評論 1 7
  • 前言: 之前項目有個模塊需要實現被通知的作用,那是第一時間就想到了觀察者,那個模塊是對象間一對一的依賴關系,...
    felicia_coder閱讀 407評論 0 2