模式開發(fā)藝術(shù)之觀察者模式

何時(shí)使用觀察者模式

1媚送、觸發(fā)聯(lián)動(dòng):當(dāng)修改目標(biāo)狀態(tài)時(shí)就會(huì)觸發(fā)相應(yīng)的通知呀癣,然后會(huì)循環(huán)調(diào)用所有注冊的觀察者對象的相應(yīng)方法镣屹。

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

1.當(dāng)一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面的操作依賴于另一個(gè)方面的狀態(tài)變化時(shí)

2.如果在封蓋一個(gè)對象的時(shí)候攘已,需要同時(shí)連帶改變其他的對象炮赦,而且不知道究竟應(yīng)該有對少對象需要被連帶改變

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

實(shí)現(xiàn)方式一:觀察者模式 經(jīng)典模版

觀察者模式的結(jié)構(gòu):Subject(被觀察者或者說是目標(biāo)類):要有添加觀察者样勃,刪除觀察者吠勘,和提醒觀察者(當(dāng)被觀察者的狀態(tài)發(fā)生改變的時(shí)候調(diào)用這個(gè)方法)的方法,Observe(觀察者):要有更新方法(當(dāng)觀察者狀態(tài)發(fā)生改變調(diào)用提醒方法后觀察者通過更新方法來做出不同響應(yīng)(動(dòng)作))峡眶。

ConcreteSubject是Subject接口的實(shí)現(xiàn)類

ConcreteObserver是Observer接口的實(shí)現(xiàn)類

目標(biāo)(被觀察者):

/**

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

*/

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);

? ? ? ? }

? ? }

}

目標(biāo)(被觀察者)具體實(shí)現(xiàn):

/**

* 具體的目標(biāo)對象,負(fù)責(zé)把有關(guān)狀態(tài)存入到相應(yīng)的觀察者對象中

*/

public class ConcreteSubject extends Subject {

? ? // 目標(biāo)對象的狀態(tài)

? ? private String Content;

? ? public String getSubjectState() {

? ? ? ? return Content;

? ? }

? ? public void setSubjectState(String Content) {

? ? ? ? this.Content = Content;

? ? ? ? // 內(nèi)容有了,通知所有的訂閱的人

? ? ? ? this.notifyObservers();

? ? }

}

觀察者接口:

/**

* 這是一個(gè)觀察者接口,定義一個(gè)更新的接口給那些在目標(biāo)發(fā)生改變的時(shí)候被通知的對象

*/

public interface Observer {

? ? /**

? ? * 更新的接口 傳入目標(biāo)對象,方便獲取相應(yīng)的目標(biāo)對象的狀態(tài)

? ? * @param subject

? ? */

? ? void update(Subject subject);

}

觀察者接口的具體實(shí)現(xiàn):

/**

* 具體的觀察者對象,實(shí)現(xiàn)更新的方法,使自身的狀態(tài)和目標(biāo)的狀態(tài)保持一致

*/

public class ConcreteObserver implements Observer {

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

? ? private String observerName;

? ? // 觀察者的狀態(tài),這個(gè)消息從目標(biāo)處獲取

? ? private String observerState;

? ? // 提醒的內(nèi)容

? ? private String remindTing;

? ? /**

? ? * 獲取目標(biāo)類的狀態(tài)同步到觀察者的狀態(tài)中

? ? */

? ? @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.創(chuàng)建目標(biāo)

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

? ? ? ? // 2.創(chuàng)建觀察者

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

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

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

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

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

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

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

? ? ? ? weather.attach(observerGirl);

? ? ? ? weather.attach(observerMum);

? ? ? ? // 4.目標(biāo)發(fā)布天氣

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

? ? }

實(shí)現(xiàn)方式二:利用Java提供的觀察者實(shí)現(xiàn) 觀察者模式

Java 實(shí)現(xiàn) VS 自己實(shí)現(xiàn)的對比四點(diǎn):

(1)不需要再定義觀察者和目標(biāo)接口(JDK已經(jīng)定義)剧防。

(2)具體的目標(biāo)實(shí)現(xiàn)里面不需要再維護(hù)觀察者的注冊信息,Java中的Observable類里面已經(jīng)實(shí)現(xiàn)辫樱。

(3)觸發(fā)通知的方式有一點(diǎn)變化峭拘,要先調(diào)用setChanged方法,這個(gè)是Java為了幫助實(shí)現(xiàn)更精確的觸發(fā)控制而提供的功能狮暑。

(4)具體觀察者的實(shí)現(xiàn)里面鸡挠,update方法其實(shí)能同時(shí)支持推模型和拉模型,這個(gè)Java在定義的時(shí)候搬男,已經(jīng)考慮拣展。

目標(biāo)(被觀察者):

/**

* 被觀察者的具體實(shí)現(xiàn)

*/

public class ConcreteSubject extends Observable {

? ? // 變化的具體內(nèi)容

? ? private String content;

? ? public String getContent() {

? ? ? ? return content;

? ? }

? ? public void setContent(String content) {

? ? ? ? this.content = content;

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

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

? ? ? ? this.setChanged();

? ? ? ? // 然后主動(dòng)通知,這里我們先用推的方式

? ? ? ? this.notifyObservers(content);

? ? ? ? // 如果是拉的方式,我們就調(diào)用

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

? ? }

}

觀察者:

/**

* 具體的觀察者對象

*/

public class ConcreteObserver implements Observer{

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

? ? private String observerName;

? ? @Override

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

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

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

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

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

? ? }

? ? public String getObserverName() {

? ? ? ? return observerName;

? ? }

? ? public void setObserverName(String observerName) {

? ? ? ? this.observerName = observerName;

? ? }

}

測試類:

public static void main(String[] args) {

? ? ? ? // 創(chuàng)建一個(gè)目標(biāo),也就是被觀察者

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

? ? ? ? // 創(chuàng)建小明的女朋友作為觀察者

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

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

? ? ? ? // 創(chuàng)建小明的老媽作為觀察者

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

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

? ? ? ? // 注冊觀察者

? ? ? ? subject.addObserver(girl);

? ? ? ? subject.addObserver(mum);

? ? ? ? // 目標(biāo)更新天氣情況了

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

? ? }

觀察者優(yōu)缺點(diǎn)

1、觀察者模式的優(yōu)點(diǎn):

(1)觀察者模式實(shí)現(xiàn)了觀察者和目標(biāo)之間的抽象耦合

(2)觀察者模式實(shí)現(xiàn)了動(dòng)態(tài)聯(lián)動(dòng)

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

2缔逛、觀察者模式的缺點(diǎn):可能會(huì)引起無謂的操作备埃。

實(shí)現(xiàn)方式三:區(qū)別對待觀察者場景問題 (靈活定制觀察者)

區(qū)別觀察者模式是,目標(biāo)父類不實(shí)現(xiàn)通知方法褐奴,在子類中實(shí)現(xiàn)有區(qū)別的通知方法瓜喇。

區(qū)別對待的觀察者模型中和通用觀察者模型的區(qū)別在于:要根據(jù)不同的觀察者來進(jìn)行不同的推送,所以區(qū)別在于目標(biāo)類中的通知更新方法需要在具體的目標(biāo)類中進(jìn)行實(shí)現(xiàn)歉糜。(因?yàn)樾枰鶕?jù)不同的情況進(jìn)行更新,所以需要在具體的目標(biāo)類中實(shí)現(xiàn)剛剛那個(gè)方法)

區(qū)別對待觀察者望众,邏輯判斷讓觀察者實(shí)現(xiàn)更符合邏輯匪补,定義到接口觀察者中,setRule()烂翰,讓小明女朋友和老媽自己去定義

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

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();

}

目標(biāo)(被觀察者)的具體實(shí)現(xiàn):

public class ConcreteSubject extends Subject {

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

? ? // 目標(biāo)對象的狀態(tài)

? ? private String Content;

? ? @Override

? ? protected void notifyObservers() {

? ? ? ? // 循環(huán)所有注冊的觀察者

? ? ? ? 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();

? ? }

}

觀察者的接口:

/**

* 定義一個(gè)更新的接口方法給那些在目標(biāo)發(fā)生改變的時(shí)候被通知的觀察者對象調(diào)用

*/

public interface Observer {

? ? // 更新的接口

? ? public void update(Subject subject);

? ? // 設(shè)置觀察者名稱

? ? public void setObserverName(String observerName);

? ? // 取得觀察者名稱

? ? public String getObserverName();

}

觀察者的具體實(shí)現(xiàn):

public class ConcreteObserver implements Observer {

? ? // 觀察者的名稱

? ? private String observerName;

? ? // 天氣情況的內(nèi)容

? ? private String content;

? ? // 提醒的內(nèi)容

? ? 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.創(chuàng)建目標(biāo)

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

? ? ? ? // 2.創(chuàng)建觀察者

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

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

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

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

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

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

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

? ? ? ? weather.attach(observerGirl);

? ? ? ? weather.attach(observerMum);

? ? ? ? // 4.目標(biāo)發(fā)布天氣

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

? ? }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末垫言,一起剝皮案震驚了整個(gè)濱河市贰剥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌筷频,老刑警劉巖蚌成,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異凛捏,居然都是意外死亡担忧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門坯癣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓶盛,“玉大人,你說我怎么就攤上這事坡锡∨钔” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵鹉勒,是天一觀的道長覆醇。 經(jīng)常有香客問我,道長刑赶,這世上最難降的妖魔是什么谆膳? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮脯倒,結(jié)果婚禮上实辑,老公的妹妹穿的比我還像新娘。我一直安慰自己藻丢,他們只是感情好剪撬,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著悠反,像睡著了一般残黑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上斋否,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天梨水,我揣著相機(jī)與錄音,去河邊找鬼茵臭。 笑死疫诽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播奇徒,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼雏亚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了逼龟?” 一聲冷哼從身側(cè)響起评凝,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腺律,沒想到半個(gè)月后奕短,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匀钧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年翎碑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片之斯。...
    茶點(diǎn)故事閱讀 38,643評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡日杈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出佑刷,到底是詐尸還是另有隱情莉擒,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布瘫絮,位于F島的核電站涨冀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏麦萤。R本人自食惡果不足惜鹿鳖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望壮莹。 院中可真熱鬧翅帜,春花似錦、人聲如沸命满。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胶台。三九已至狭莱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間概作,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工默怨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留讯榕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像愚屁,于是被迫代替她去往敵國和親济竹。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 觀察者模式定義了一種一對多的依賴關(guān)系霎槐,讓多個(gè)觀察者對象同時(shí)監(jiān)聽某一個(gè)主題對象送浊。這個(gè)主題對象在狀態(tài)上發(fā)生變化時(shí),會(huì)通...
    扈扈哈嘿閱讀 1,316評論 0 12
  • 觀察者模式也叫作發(fā)布-訂閱模式丘跌,也就是事件監(jiān)聽機(jī)制袭景。觀察者模式定義了一種一對多的依賴關(guān)系,讓多個(gè)觀察者對象同時(shí)監(jiān)聽...
    超級大雞腿閱讀 231評論 0 0
  • 設(shè)計(jì)模式分類 總體來說設(shè)計(jì)模式分為三大類:創(chuàng)建型模式闭树,共五種:工廠方法模式耸棒、抽象工廠模式、單例模式报辱、建造者模式与殃、原...
    lifeline丿毅閱讀 1,202評論 0 2
  • 觀察者模式是使用最為頻繁的設(shè)計(jì)模式之一。在很多地方都有用到碍现。比如各種編程語言的GUI事件處理實(shí)現(xiàn)幅疼,各種框架的實(shí)現(xiàn),...
    樹獺非懶閱讀 15,635評論 1 7
  • 前言: 之前項(xiàng)目有個(gè)模塊需要實(shí)現(xiàn)被通知的作用昼接,那是第一時(shí)間就想到了觀察者爽篷,那個(gè)模塊是對象間一對一的依賴關(guān)系,...
    felicia_coder閱讀 397評論 0 2