观察者模式(Observer) 简介
一, 觀察者模式(Observer) 的定義
觀察者模式:?? 定義了一種 1對(duì)多 的依賴關(guān)系, 讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽1個(gè)主題對(duì)象.
??????????????????????? 這個(gè)主題對(duì)象在狀態(tài)發(fā)生變化時(shí), 會(huì)通知所有的觀察者對(duì)象, 使它們能夠同時(shí)更新自己.
稍微解釋一下 這個(gè)1 對(duì)多 的依賴關(guān)系.
?1對(duì)多 這個(gè)關(guān)鍵詞我們常常在DB 表設(shè)計(jì)里提到,? 但是這里的意思是有點(diǎn)區(qū)別的.
????????????????????
首先,? 1 是1個(gè)對(duì)象, 而不是1個(gè)類,???? 而多也是指多個(gè)對(duì)象, 而不是多個(gè)類.
其次,? 這里的多個(gè)對(duì)象可以是多個(gè)不同的類的對(duì)象.? 甚至是毫無(wú)關(guān)系的多個(gè)類.
再次, 這個(gè)依賴關(guān)系, 到底是1個(gè)對(duì)象依賴多個(gè)對(duì)象, 還是多個(gè)對(duì)象依賴1個(gè)對(duì)象呢.
在這里的定義來(lái)講答案是后者.
但是, 實(shí)際上, 1個(gè)觀察者也可以觀察多個(gè)被觀察者的. (但這就不屬于觀察者模式了)
所以, 觀察者模式(Observer) 也叫做 發(fā)布-訂閱模式(publish/Subscribe).
相當(dāng)與, 多個(gè)讀者同時(shí)收聽1個(gè)電臺(tái).
二, 觀察者模式(Observer) 的各個(gè)角色.
首先睇下Observer 模式的標(biāo)準(zhǔn)UML圖.
我們大概得出上圖有4個(gè)角色.
2.1 Observer (觀察者)
Observer是1個(gè)接口, 我們可以理解它是1個(gè)抽象觀察者類.
它只有1個(gè)update()方法, 也就是說(shuō)它可以通過(guò)這個(gè)方法來(lái)執(zhí)行某些動(dòng)作.
2.2 Subject (通知者/被觀察者)
Subject是1個(gè)接口, 我們可以理解為1個(gè)抽象被觀察者類.我們可以見到它有5個(gè)方法.
attach() 和 detach()方法用來(lái)增刪觀察者的數(shù)量, 也就是指當(dāng)前被觀察者對(duì)象到底有多少個(gè)觀察者在觀察著它.
setState(), 和 getState() 用于獲取和設(shè)置通知者對(duì)象本身的狀態(tài), 這個(gè)狀態(tài)通常是傳送給觀察者們的信息或參數(shù).
也就是說(shuō)觀察者模式到底在觀察什么. 無(wú)非就是觀察被觀察者的這個(gè)狀態(tài).
Notify(),? 被觀察者通知所有觀察者, 讓觀察者根據(jù)自己的當(dāng)前狀態(tài)(getState())執(zhí)行自己的update()方法
2.3 ConcreteSubject (具體通知者/被觀察者)
這個(gè)(些)就是具體的被觀察者類, 只要實(shí)現(xiàn)了Subject接口, 就可以添加1些對(duì)象作為自己的觀察者(或者叫粉絲啦)
寫到這里, 大家都會(huì)了解到, 這個(gè)類里面肯定有1個(gè)容器, 用于存放觀察者的對(duì)象.
這個(gè)容器可以根據(jù)需要由具體的被觀察者選擇, 通常是無(wú)序不能重復(fù)的Set容器(例如 HashSet)
而Notify()方法無(wú)非就是遍歷自己的觀察者容器, 逐個(gè)執(zhí)行觀察者的update()方法.
2.4 ConcreteObserver (具體觀察者類)
這些類可以是毫無(wú)關(guān)聯(lián)的類, 但是它們都必須實(shí)現(xiàn)Observer接口一旦這些對(duì)象被通知者, 加入自己的容器, 就相當(dāng)于觀察者正在觀察某個(gè)被觀察者.
注意,? 觀察者可以被多個(gè)被觀察者加入自己的容器, 也就是相當(dāng)于觀察了多個(gè)被觀察者了.(但這就break了觀察者模式)
三, 1個(gè)具體例子和代碼.
下面我們用1個(gè)具體例子來(lái)簡(jiǎn)單實(shí)現(xiàn)這個(gè)模式.
我們假定1個(gè)事件有3個(gè)角色.
1. 指揮者.(Commander)
??????????? 指揮炮手打炮, 他可以讓讓 若干個(gè)炮手和炮灰納入自己的命令范圍.
2. 炮手, (CannonShooter)
????????? 一旦指揮者通知目標(biāo), 若干個(gè)炮手就往哪個(gè)目標(biāo)轟擊.
3. 炮灰 (CannonFodder)
???????? 一旦指揮者通知, 炮灰就趴下..
也就是說(shuō), 炮手必須知道指揮這的狀態(tài)(目標(biāo)信號(hào)), 到底打誰(shuí).
而炮灰是無(wú)序關(guān)心到底打哪里的, 一旦接到通知, 趴下就是了.
3.1 UML圖
3.2 Subject接口 代碼
public interface Subject {public void attach(Observer obs);public void detach(Observer obs);public void sNotify(); //notify is a finel method of Object classpublic int getState();public void setState(int state); }5個(gè)方法的意義上面已經(jīng)解釋過(guò)了.
通知方法之所以不寫成notify(), 是因?yàn)閚otify()本身是Object類的1個(gè)finel方法
3.2 Observer接口 代碼
public interface Observer {public void update(); }只有1個(gè)抽象方法update()
3.3 Commander 類 代碼
import java.util.HashSet; import java.util.Iterator; public class Commander implements Subject{private int targetPlaceID;private HashSet<Observer> gunnerSet = new HashSet<Observer>();@Overridepublic void attach(Observer obs){this.gunnerSet.add(obs);}@Overridepublic void detach(Observer obs) {this.gunnerSet.remove(obs);}@Overridepublic void sNotify() {if (this.gunnerSet.isEmpty()){return;}Iterator itr = this.gunnerSet.iterator();while (itr.hasNext()){Observer obs = (Observer)itr.next();obs.update();}}@Overridepublic int getState() {// TODO Auto-generated method stubreturn this.targetPlaceID;}@Overridepublic void setState(int state) {// TODO Auto-generated method stubthis.targetPlaceID = state;}}它重寫了接口所有方法.
所謂的notify()方法, 無(wú)非就是遍歷自己容器的所有觀察者, 該干嘛的干嘛(遍歷調(diào)用它們的update())方法
3.4 CannonShooter 類 代碼
public class CannonShooter implements Observer{private Subject cmder;public CannonShooter(Subject cmder){this.cmder = cmder;}public Subject getCmder() {return cmder;}public void setCmder(Subject cmder) {this.cmder = cmder;}public void fireCannon(int targetPlace){System.out.println(this.getClass().getSimpleName() + ": fired on target(id:" + targetPlace + ") by Cannon");}@Overridepublic void update() {// TODO Auto-generated method stubfireCannon(cmder.getState());} }可以見到,? 炮手必須知道指揮者的狀態(tài)信息, 所以它里面必須有個(gè)當(dāng)前指揮者的對(duì)象成員.
3.5 CannonFodder 類 代碼
public class CannonFodder implements Observer{private int id;public CannonFodder(int id){this.id = id;}public void getDown(){System.out.println(this.getClass().getSimpleName() +" id:"+ this.id + " getDowned");}@Overridepublic void update() {// TODO Auto-generated method stubthis.getDown();} }炮灰無(wú)需關(guān)心指揮者的狀態(tài), 里面只需要重寫自己的update()方法就ok.
3.6 CannonFodder 類 代碼
public class ClientObserver {public static void f(){Commander cmder = new Commander();CannonShooter cster = new CannonShooter(cmder);CannonFodder cfder1 = new CannonFodder(1);CannonFodder cfder2 = new CannonFodder(2);CannonFodder cfder3 = new CannonFodder(3);cmder.setState(107);cmder.attach(cster);cmder.attach(cfder1);cmder.attach(cfder2);cmder.attach(cfder3);cmder.sNotify();cmder.setState(108);cmder.detach(cfder3);cmder.sNotify();} }
上面的代碼不難看懂.
無(wú)非就是實(shí)例化1個(gè)指揮者, 1個(gè)炮手, 3個(gè)炮灰
首先, 指揮者通知向107目標(biāo)打炮,?? 炮手射了, 3個(gè)炮灰趴下了.
后來(lái)指揮者想向打擊108目標(biāo), 但是覺得第3號(hào)炮灰不在攻擊范圍,?? 所以從自己的觀察者容器里移除3號(hào)炮灰.
這時(shí), 炮手向108號(hào)目標(biāo)打擊,? 只有1號(hào)2號(hào)炮灰聽指揮爬下.
相當(dāng)靈活.
四, 觀察者模式的特點(diǎn)和應(yīng)用范圍.
4.1 Observer模式的特點(diǎn)
上面的例子中, 觀察者和被觀察者的耦合性不大.
1個(gè)subject可以有任意數(shù)目的觀察者Observer,? 程序猿令subject發(fā)出通知時(shí)根本無(wú)需知道觀察者是誰(shuí), 有多少觀察者存在.
而單個(gè)觀察者本身也無(wú)需知道到底有幾個(gè)其他觀察者同時(shí)存在.
4.2 什么時(shí)候應(yīng)該使用Observer模式
很明顯嘛,? 就是當(dāng)1個(gè)對(duì)象發(fā)生改變的同時(shí)需要同時(shí)改變其他多個(gè)對(duì)象時(shí).
而且, 觀察者模式令到耦合的雙方, 依賴與接口(抽象), 而不是依賴于具體(非抽象類), 符合封閉-開放模式.
總結(jié)
以上是生活随笔為你收集整理的观察者模式(Observer) 简介的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 建造者模式简介
- 下一篇: 为什么有人说面向对象编程就是面向接口编程