【12】行为型-观察者模式
為什么80%的碼農都做不了架構師?>>> ??
1、上下文及定義
定義對象間的一種一對多的依賴關系。當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并被自動更新。
2、常用場景
為了當對象的狀態發生改變時,所有依賴它的對象得到通知并進行相應處理。
被觀察的對象稱之為Subject,觀察者成為Observer
(1)圖形化編程
(2)分布式事件處理
3、解決方法
推模型:目標對象主動向觀察者推動目標的詳細信息,不管觀察者是否需要,推動的信息通常是目標對象的全部或部分數據,相當于是在廣播通信。
?
拉模型:目標對象在通知觀察者的時候,只傳遞少量信息,如果觀察者需要更具體的信息,由觀察者主動到目標對象中獲取,相當于是觀察者從目標對象中拉數據。一般這種模型的實現中,會把目標對象自身通過update方法傳遞給觀察者,這樣在觀察者需要獲取數據的時候,就可以通過這個引用來獲取了。
推模型假定目標對象知道觀察者需要的數據,
而拉模型是目標對象不知道觀察者具體需要什么數據,沒有辦法的情況下,干脆把自己傳遞給觀察者,讓觀察者自己去按需取值。
?
推模型可能會使得觀察者對象難以復用,因為觀察者定義的update方法是按需而定義的,可能無法兼顧沒有考慮到的使用情況,當新情況出現的時候,就可能需要提供新的update方法,或者干脆重新實現觀察者。
?
拉模型update參數是目標對象本身,是能夠傳遞的最大數據集合了,基本上可以適應各種情況的需要。
4、抽象模型
5、代碼實例
(1)拉模型實例
/** *?目標對象,作為被觀察者 */ public?class?Subject?{/***?用來保存注冊的觀察者對象,也就是報紙的訂閱者*/private?List<Observer>?readers?=?new?ArrayList<Observer>();/***?報紙的讀者需要先向報社訂閱,先要注冊*?@param?reader?報紙的讀者?*?@return?是否注冊成功*/public?void?attach(Observer?reader)?{readers.add(reader);}/***?報紙的讀者可以取消訂閱*?@param?reader?報紙的讀者*?@return?是否取消成功*/public?void?detach(Observer?reader)?{readers.remove(reader);}/***?當每期報紙印刷出來后,就要迅速的主動的被送到讀者的手中,*?相當于通知讀者,讓他們知道*/protected?void?notifyObservers()?{for(Observer?reader?:?readers){reader.update(this);}} }具體subject
/** *?報紙對象,具體的目標實現 */ public?class?NewsPaper?extends?Subject{/***?報紙的具體內容*/private?String?content;/***?獲取報紙的具體內容*?@return?報紙的具體內容*/public?String?getContent()?{return?content;}/***?示意,設置報紙的具體內容,相當于要出版報紙了*?@param?content?報紙的具體內容*/public?void?setContent(String?content)?{this.content?=?content;//內容有了,說明又出報紙了,那就通知所有的讀者notifyObservers();} }觀察者
/** *?觀察者,比如報紙的讀者 */ public?interface?Observer?{/***?被通知的方法*?@param?subject?具體的目標對象,可以獲取報紙的內容*/public?void?update(Subject?subject); }觀察者實現
客戶端
public?class?Client?{public?static?void?main(String[]?args)?{//創建一個報紙,作為被觀察者NewsPaper?subject?=?new?NewsPaper();//創建閱讀者,也就是觀察者Reader?reader1?=?new?Reader();reader1.setName("張三");Reader?reader2?=?new?Reader();reader2.setName("李四");Reader?reader3?=?new?Reader();reader3.setName("王五");//注冊閱讀者subject.attach(reader1);subject.attach(reader2);subject.attach(reader3);//要出報紙啦subject.setContent("本期內容是觀察者模式");} }(2)推模型實例
觀察者
觀察者實現
/** *?真正的讀者,為了簡單就描述一下姓名 */ public?class?Reader?implements?Observer{/***?讀者的姓名*/private?String?name;public?void?update(String?content)?{//這是采用推的方式System.out.println(name+"收到報紙了,閱讀先。內容是==="+content);}public?String?getName()?{return?name;}public?void?setName(String?name)?{this.name?=?name;} }Subject類
/** *?目標對象,作為被觀察者,使用推模型 */ public?class?Subject?{/***?用來保存注冊的觀察者對象,也就是報紙的訂閱者*/private?List<Observer>?readers?=?new?ArrayList<Observer>();/***?報紙的讀者需要先向報社訂閱,先要注冊*?@param?reader?報紙的讀者?*?@return?是否注冊成功*/public?void?attach(Observer?reader)?{readers.add(reader);}/***?報紙的讀者可以取消訂閱*?@param?reader?報紙的讀者*?@return?是否取消成功*/public?void?detach(Observer?reader)?{readers.remove(reader);}/***?當每期報紙印刷出來后,就要迅速的主動的被送到讀者的手中,*?相當于通知讀者,讓他們知道*?@param?content?要主動推送的內容*/protected?void?notifyObservers(String?content)?{for(Observer?reader?:?readers){reader.update(content);}} }subject類實現
/** *?報紙對象,具體的目標實現 */ public?class?NewsPaper?extends?Subject{/***?報紙的具體內容*/private?String?content;/***?獲取報紙的具體內容*?@return?報紙的具體內容*/public?String?getContent()?{return?content;}/***?示意,設置報紙的具體內容,相當于要出版報紙了*?@param?content?報紙的具體內容*/public?void?setContent(String?content)?{this.content?=?content;//內容有了,說明又出報紙了,那就通知所有的讀者notifyObservers(content);} }(3)JDK觀察者模式
Java的util包里面有一個類Observable,它實現了大部分我們需要的目標的功能;
還有一個Observer,定義了update方法,就是觀察者的接口。
?
1、不需要再定義Subject和Observer類
2、具體的Subject實現類里面不再需要維護Observer的注冊信息
3、觸發通知方式有點變化,需要先調用setChanged方法
4、具體的Observer實現類里面,update方法能同時指出推模型和拉模型
Subject實現類
Obsever實現類
/** *?真正的讀者,為了簡單就描述一下姓名 */ public?class?Reader?implements?java.util.Observer{/***?讀者的姓名*/private?String?name;public?String?getName()?{return?name;}public?void?setName(String?name)?{this.name?=?name;}public?void?update(Observable?o,?Object?obj)?{//這是采用推的方式System.out.println(name+"收到報紙了,閱讀先。目標推過來的內容是==="+obj);//這是獲取拉的數據System.out.println(name+"收到報紙了,閱讀先。主動到目標對象去拉的內容是==="+((NewsPaper)o).getContent());}}客戶端
public?class?Client?{public?static?void?main(String[]?args)?{//創建一個報紙,作為被觀察者NewsPaper?subject?=?new?NewsPaper();//創建閱讀者,也就是觀察者Reader?reader1?=?new?Reader();reader1.setName("張三");Reader?reader2?=?new?Reader();reader2.setName("李四");Reader?reader3?=?new?Reader();reader3.setName("王五");//注冊閱讀者subject.addObserver(reader1);subject.addObserver(reader2);subject.addObserver(reader3);//要出報紙啦subject.setContent("本期內容是觀察者模式");} }(4)Observable類
/***?If?this?object?has?changed,?as?indicated?by?the*?<code>hasChanged</code>?method,?then?notify?all?of?its?observers*?and?then?call?the?<code>clearChanged</code>?method?to?indicate*?that?this?object?has?no?longer?changed.*?<p>*?Each?observer?has?its?<code>update</code>?method?called?with?two*?arguments:?this?observable?object?and?the?<code>arg</code>?argument.**?@param???arg???any?object.*?@see?????java.util.Observable#clearChanged()*?@see?????java.util.Observable#hasChanged()*?@see?????java.util.Observer#update(java.util.Observable,?java.lang.Object)*/public?void?notifyObservers(Object?arg)?{/**?a?temporary?array?buffer,?used?as?a?snapshot?of?the?state?of*?current?Observers.*/Object[]?arrLocal;synchronized?(this)?{/*?We?don't?want?the?Observer?doing?callbacks?into*?arbitrary?code?while?holding?its?own?Monitor.*?The?code?where?we?extract?each?Observable?from*?the?Vector?and?store?the?state?of?the?Observer*?needs?synchronization,?but?notifying?observers*?does?not?(should?not).??The?worst?result?of?any*?potential?race-condition?here?is?that:*?1)?a?newly-added?Observer?will?miss?a*???notification?in?progress*?2)?a?recently?unregistered?Observer?will?be*???wrongly?notified?when?it?doesn't?care*/if?(!changed)return;arrLocal?=?obs.toArray();clearChanged();}for?(int?i?=?arrLocal.length-1;?i>=0;?i--)((Observer)arrLocal[i]).update(this,?arg);}/***?Marks?this?<tt>Observable</tt>?object?as?having?been?changed;?the*?<tt>hasChanged</tt>?method?will?now?return?<tt>true</tt>.*/protected?synchronized?void?setChanged()?{changed?=?true;}/***?Indicates?that?this?object?has?no?longer?changed,?or?that?it?has*?already?notified?all?of?its?observers?of?its?most?recent?change,*?so?that?the?<tt>hasChanged</tt>?method?will?now?return?<tt>false</tt>.*?This?method?is?called?automatically?by?the*?<code>notifyObservers</code>?methods.**?@see?????java.util.Observable#notifyObservers()*?@see?????java.util.Observable#notifyObservers(java.lang.Object)*/protected?synchronized?void?clearChanged()?{changed?=?false;}? 采用倒序的方式通知。
? 采用JDK的觀察者模式的限制:
A、繼承Observable類,如果需要多繼承的話,只能采用新類繼承它,然后組合新累,調用setChanged方法,因為該方法時protected的
B、如果需要更改通知順序的話,需要自己專門來處理
轉載于:https://my.oschina.net/scipio/blog/284640
總結
以上是生活随笔為你收集整理的【12】行为型-观察者模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: json数据解析详解---代码每行进行分
- 下一篇: Sylius不需要缓存使用默认地址