观察者模式(Observer) 简介
一, 觀察者模式(Observer) 的定義
觀察者模式:?? 定義了一種 1對多 的依賴關(guān)系, 讓多個觀察者對象同時監(jiān)聽1個主題對象.
??????????????????????? 這個主題對象在狀態(tài)發(fā)生變化時, 會通知所有的觀察者對象, 使它們能夠同時更新自己.
稍微解釋一下 這個1 對多 的依賴關(guān)系.
?1對多 這個關(guān)鍵詞我們常常在DB 表設(shè)計里提到,? 但是這里的意思是有點區(qū)別的.
????????????????????
首先,? 1 是1個對象, 而不是1個類,???? 而多也是指多個對象, 而不是多個類.
其次,? 這里的多個對象可以是多個不同的類的對象.? 甚至是毫無關(guān)系的多個類.
再次, 這個依賴關(guān)系, 到底是1個對象依賴多個對象, 還是多個對象依賴1個對象呢.
在這里的定義來講答案是后者.
但是, 實際上, 1個觀察者也可以觀察多個被觀察者的. (但這就不屬于觀察者模式了)
所以, 觀察者模式(Observer) 也叫做 發(fā)布-訂閱模式(publish/Subscribe).
相當與, 多個讀者同時收聽1個電臺.
二, 觀察者模式(Observer) 的各個角色.
首先睇下Observer 模式的標準UML圖.
我們大概得出上圖有4個角色.
2.1 Observer (觀察者)
Observer是1個接口, 我們可以理解它是1個抽象觀察者類.
它只有1個update()方法, 也就是說它可以通過這個方法來執(zhí)行某些動作.
2.2 Subject (通知者/被觀察者)
Subject是1個接口, 我們可以理解為1個抽象被觀察者類.我們可以見到它有5個方法.
attach() 和 detach()方法用來增刪觀察者的數(shù)量, 也就是指當前被觀察者對象到底有多少個觀察者在觀察著它.
setState(), 和 getState() 用于獲取和設(shè)置通知者對象本身的狀態(tài), 這個狀態(tài)通常是傳送給觀察者們的信息或參數(shù).
也就是說觀察者模式到底在觀察什么. 無非就是觀察被觀察者的這個狀態(tài).
Notify(),? 被觀察者通知所有觀察者, 讓觀察者根據(jù)自己的當前狀態(tài)(getState())執(zhí)行自己的update()方法
2.3 ConcreteSubject (具體通知者/被觀察者)
這個(些)就是具體的被觀察者類, 只要實現(xiàn)了Subject接口, 就可以添加1些對象作為自己的觀察者(或者叫粉絲啦)
寫到這里, 大家都會了解到, 這個類里面肯定有1個容器, 用于存放觀察者的對象.
這個容器可以根據(jù)需要由具體的被觀察者選擇, 通常是無序不能重復(fù)的Set容器(例如 HashSet)
而Notify()方法無非就是遍歷自己的觀察者容器, 逐個執(zhí)行觀察者的update()方法.
2.4 ConcreteObserver (具體觀察者類)
這些類可以是毫無關(guān)聯(lián)的類, 但是它們都必須實現(xiàn)Observer接口一旦這些對象被通知者, 加入自己的容器, 就相當于觀察者正在觀察某個被觀察者.
注意,? 觀察者可以被多個被觀察者加入自己的容器, 也就是相當于觀察了多個被觀察者了.(但這就break了觀察者模式)
三, 1個具體例子和代碼.
下面我們用1個具體例子來簡單實現(xiàn)這個模式.
我們假定1個事件有3個角色.
1. 指揮者.(Commander)
??????????? 指揮炮手打炮, 他可以讓讓 若干個炮手和炮灰納入自己的命令范圍.
2. 炮手, (CannonShooter)
????????? 一旦指揮者通知目標, 若干個炮手就往哪個目標轟擊.
3. 炮灰 (CannonFodder)
???????? 一旦指揮者通知, 炮灰就趴下..
也就是說, 炮手必須知道指揮這的狀態(tài)(目標信號), 到底打誰.
而炮灰是無序關(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個方法的意義上面已經(jīng)解釋過了.
通知方法之所以不寫成notify(), 是因為notify()本身是Object類的1個finel方法
3.2 Observer接口 代碼
public interface Observer {public void update(); }只有1個抽象方法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()方法, 無非就是遍歷自己容器的所有觀察者, 該干嘛的干嘛(遍歷調(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)信息, 所以它里面必須有個當前指揮者的對象成員.
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();} }炮灰無需關(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();} }
上面的代碼不難看懂.
無非就是實例化1個指揮者, 1個炮手, 3個炮灰
首先, 指揮者通知向107目標打炮,?? 炮手射了, 3個炮灰趴下了.
后來指揮者想向打擊108目標, 但是覺得第3號炮灰不在攻擊范圍,?? 所以從自己的觀察者容器里移除3號炮灰.
這時, 炮手向108號目標打擊,? 只有1號2號炮灰聽指揮爬下.
相當靈活.
四, 觀察者模式的特點和應(yīng)用范圍.
4.1 Observer模式的特點
上面的例子中, 觀察者和被觀察者的耦合性不大.
1個subject可以有任意數(shù)目的觀察者Observer,? 程序猿令subject發(fā)出通知時根本無需知道觀察者是誰, 有多少觀察者存在.
而單個觀察者本身也無需知道到底有幾個其他觀察者同時存在.
4.2 什么時候應(yīng)該使用Observer模式
很明顯嘛,? 就是當1個對象發(fā)生改變的同時需要同時改變其他多個對象時.
而且, 觀察者模式令到耦合的雙方, 依賴與接口(抽象), 而不是依賴于具體(非抽象類), 符合封閉-開放模式.
總結(jié)
以上是生活随笔為你收集整理的观察者模式(Observer) 简介的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 建造者模式简介
- 下一篇: 为什么有人说面向对象编程就是面向接口编程