设计模式记--Observer Pattern观察者模式
? 觀察者模式——定義了對(duì)象之間的一對(duì)多依賴,這樣一來,當(dāng)一個(gè)對(duì)像改變狀態(tài)時(shí),它的所有依賴者都會(huì)收到通知并自動(dòng)更新.
? 從定義可以看出,OBSERVER(觀察者)模式邏輯上需要兩組對(duì)象來實(shí)現(xiàn).首先它必需要有發(fā)布者(Publish),也可稱為被觀察的目標(biāo) (Subject)(習(xí)慣上都稱它為目標(biāo)Subject,后面我們都稱它作目標(biāo)Subject),另外就是訂閱者(Subscribe),習(xí)慣上稱為觀察 者(Observer).一個(gè)目標(biāo)對(duì)象對(duì)應(yīng)多個(gè)觀察者對(duì)象,目標(biāo)對(duì)象發(fā)生變化時(shí),所有在目標(biāo)對(duì)象中注冊(cè)的觀察者對(duì)象會(huì)得到通知,自動(dòng)更新自己.
? 觀察者模式UML圖如下:
????????????????????????
??
? 觀察者模式的相關(guān)角色:
? 1、抽象主體(Subject)角色:也 就是被關(guān)注的對(duì)象,是一對(duì)多關(guān)系中的那個(gè)“一”。它的相關(guān)信息的變化將會(huì)通知給訂閱這個(gè)變化的觀察者。主體角色把所有對(duì)觀察考對(duì)象的引用保存在一個(gè)集合 (List,ArrayList.....)里,每個(gè)主體可能管理若干數(shù)量的觀察者。抽象主體提供一個(gè)接口,可以增加和刪除觀察者對(duì)象,主體角色又叫做抽 象被觀察者(Observable)角色,一般用一個(gè)抽象類或者一個(gè)接口實(shí)現(xiàn)。
? 2、抽象觀察者(Observer)角色:為所有的具體觀察者定義一個(gè)接口,在得到主體的通知時(shí)更新自己。這個(gè)接口叫做更新接口(Update)。抽象觀察者角色一般用一個(gè)抽象類或者一個(gè)接口實(shí)現(xiàn)。在這個(gè)示意性的實(shí)現(xiàn)中,更新接口只包含一個(gè)方法(即Update()方法),這個(gè)方法叫做更新方法。
? 3、具體主體(ConcreteSubject)角色:將有關(guān)狀態(tài)存入具體現(xiàn)察者對(duì)象;在具體主體的內(nèi)部狀態(tài)改變時(shí),給所有登記過的觀察者發(fā)出通知。具體主體角色又叫做具體被觀察者角色(Concrete Observable)。具體主題角色通常用一個(gè)具體子類實(shí)現(xiàn)。
? 4、具體觀察者(ConcreteObserver)角色:存儲(chǔ)與主體的狀態(tài)自恰的狀態(tài)。具體現(xiàn)察者角色實(shí)現(xiàn)抽象觀察者角色所要求的更新接口,以便使本身的狀態(tài)與主體的狀態(tài)相協(xié)調(diào)。如果需要,具體現(xiàn)察者角色可以保存一個(gè)指向具體主體對(duì)象的引用。具體觀察者角色通常用一個(gè)具體子類實(shí)現(xiàn)。
?下面,我們用代碼來示例觀察者模式。
?程序如下圖:
?????????????????????????
?一、觀察者模式的基本思路
? 1、抽象主體Subject
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
using?System.Collections;
namespace?MyObserver
{
????//定義Subject抽象類,它是'ConcreteSubject'具體目標(biāo)對(duì)象的基類
????//要實(shí)現(xiàn)Observer模式時(shí),通常將數(shù)據(jù)對(duì)象作為目標(biāo)(Subject),各個(gè)顯示數(shù)據(jù)的對(duì)象作為觀察者Observer
????//每一個(gè)觀察者(Observer)通過調(diào)用目標(biāo)(Subject)中的一個(gè)公有(public)方法,在他所感興趣的數(shù)據(jù)中注冊(cè)(registers)自己。
????//這樣,當(dāng)數(shù)據(jù)改變時(shí),每一個(gè)目標(biāo)(Subject)通過觀察者(Observer)的接口發(fā)送更新通知。
????abstract?class?Subject
????{?
????????#region?定義一個(gè)List來裝盛所有與此數(shù)據(jù)對(duì)象相聯(lián)系的觀察者Observer
????????private?List<Observer>?_observers?=?new?List<Observer>();
????????#endregion
????????#region?附加或解除Observer功能(注冊(cè)或注銷功能)
????????public?void?Attach(Observer?observer)
????????{
????????????_observers.Add(observer);?//observer觀察者在此數(shù)據(jù)對(duì)象中注冊(cè)(registers)自己。
????????}
????????public?void?Detach(Observer?observer)
????????{
????????????_observers.Remove(observer);//在此數(shù)據(jù)對(duì)象中取消注冊(cè),也即讓對(duì)象變更時(shí)不用再通知此observer觀察者
????????}
????????#endregion
????????#region?通知在_observers列表中的所有觀察者
????????public?void?Nofity()
????????{
????????????//遍歷觀察者列表,按列表的名錄逐一通知
????????????foreach?(Observer?o?in?_observers)
????????????{
????????????????o.Update();
????????????}
????????}
????????#endregion
????}
?
}
? 2、具體主體ConcreteSubject?
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyObserver
{
????//定義具體數(shù)據(jù)對(duì)象類,它繼承自Subject抽象類
????class?ConcreteSubject:Subject
????{
????????#region?SubjectState屬性
????????private?string?_subjectState;
????????public?string?SubjectState
????????{
????????????get?{?return?_subjectState;?}
????????????set?{?_subjectState?=?value;?}
????????}
????????#endregion
????}
}
? 3、抽象觀察者Observer
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyObserver
{
????#region?定義Observer抽象類,它是ConcreteObserver的基類
????abstract??class?Observer
????{
????????//定義一個(gè)用于發(fā)送更新通知的接口
????????//這樣,當(dāng)數(shù)據(jù)改變時(shí),每一個(gè)目標(biāo)(Subject)通過觀察者(Observer)的接口發(fā)送更新通知。
????????public?abstract?void?Update();
????}
????#endregion
}
? 4、具體觀察者ConcreteObserver
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyObserver
{
????class?ConcreteObserver:Observer?
????{
????????private?string?_name;
????????private?string?_observerState;
????????private?ConcreteSubject?_subject;
????????public?ConcreteSubject?Subject
????????{
????????????get?{?return?_subject;?}
????????????set?{?_subject?=?value;?}
????????}
????????#region?構(gòu)造函數(shù)
????????public?ConcreteObserver(ConcreteSubject?subject,?string?name)
????????{
????????????this._subject?=?subject;
????????????this._name?=?name;
????????}
????????#endregion
????????#region?實(shí)現(xiàn)目標(biāo)數(shù)據(jù)更新通知接口
????????public?override?void?Update()
????????{
????????????_observerState?=?_subject.SubjectState;
????????????Console.WriteLine("觀察者?{0}?收到的數(shù)據(jù)對(duì)象的新狀態(tài)值是?{1}",_name,_observerState);
????????}
????????#endregion
????}
}
? 5、客戶端代碼?
????????????#region?基本思路示例
????????????Console.WriteLine("----------觀察者模式基本思路示例--------");
????????????ConcreteSubject?s?=?new?ConcreteSubject();?//首先創(chuàng)建一個(gè)數(shù)據(jù)對(duì)象
????????????s.Attach(new?ConcreteObserver(s,?"X"));?//向這個(gè)數(shù)據(jù)對(duì)象內(nèi)注冊(cè)三個(gè)觀察者X,Y,Z
????????????s.Attach(new?ConcreteObserver(s,?"Y"));
????????????s.Attach(new?ConcreteObserver(s,?"Z"));
????????????s.SubjectState?=?"ABC";?//改變數(shù)據(jù)對(duì)象的狀態(tài)值
????????????s.Nofity();?//調(diào)用數(shù)據(jù)對(duì)象的通知功能來依次通知已經(jīng)注冊(cè)的觀察者
????????????Console.ReadKey();
????????????#endregion
?二、我的團(tuán)長我的團(tuán)使用觀察者模式
?這里,我們讓孟煩了在前哨望風(fēng),當(dāng)他發(fā)現(xiàn)敵情時(shí),他馬上通知所有兄弟們準(zhǔn)備戰(zhàn)斗。這里,孟煩了就是具體主體ConcreteSubject,他的那些兄弟:要麻,迷龍,豆餅....等等都是具體觀察者(ConcreteObserver)
? 1、抽象主體Subject:Guard
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
using?System.Collections;
namespace?MyObserver
{
????abstract?class?Guard
????{
????????#region?定義一個(gè)List來收集所有需要通知到的其它士兵
????????private?List<Soldier>?_soldiers?=?new?List<Soldier>();
????????#endregion
????????#region?附加或解除Observer功能(注冊(cè)或注銷功能)
????????public?void?Attach(Soldier?observer)
????????{
????????????_soldiers.Add(observer);?//observer觀察者在此數(shù)據(jù)對(duì)象中注冊(cè)(registers)自己。
????????}
????????public?void?Detach(Soldier?observer)
????????{
????????????_soldiers.Remove(observer);//在此數(shù)據(jù)對(duì)象中取消注冊(cè),也即讓對(duì)象變更時(shí)不用再通知此observer觀察者
????????}
????????#endregion
????????#region?通知在_observers列表中的所有觀察者
????????public?void?Nofity()
????????{
????????????//遍歷觀察者列表,按列表的名錄逐一通知
????????????foreach?(Soldier?o?in?_soldiers)
????????????{
????????????????o.Update();
????????????}
????????}
????????#endregion
????}
}
? 2、具體主體ConcreteSubject:ConcreteGurad
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyObserver
{
????class?ConcreteGurad:Guard
????{
????????private?string?_name;
????????public?string?Name
????????{
????????????get?{?return?_name;?}
????????????set?{?_name?=?value;?}
????????}
????????#region?FightInfo屬性
????????private?string??_fightInfo;
????????public?string?FightInfo
????????{
????????????get?{?return?_fightInfo;?}
????????????set?{?_fightInfo?=?value;?}
????????}
????????#endregion
????????public?ConcreteGurad(string?name)
????????{
????????????this._name?=?name;
????????}
????}
}
? 3、抽象觀察者Observer:Soldier
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyObserver
{
????abstract??class?Soldier
????{
????????public?abstract?void?Update();
????}
}
? 4、具體觀察者ConcreteObserver:ConcreteSoldier
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyObserver
{
????class?ConcreteSoldier:Soldier?
????{
????????private?string?_observerState;
????????private?string?_name;
????????private?ConcreteGurad?_subject;
????????public?ConcreteGurad?Subject
????????{
????????????get?{?return?_subject;?}
????????????set?{?_subject?=?value;?}
????????}
????????#region?構(gòu)造函數(shù)
????????public?ConcreteSoldier(ConcreteGurad?subject,?string?name)
????????{
????????????this._subject?=?subject;
????????????this._name?=?name;
????????}
????????#endregion
????????#region?實(shí)現(xiàn)目標(biāo)數(shù)據(jù)更新通知接口
????????public?override?void?Update()
????????{
????????????_observerState?=?_subject.FightInfo?;
????????????Console.WriteLine("士兵?'{0}'??收到哨兵?'{1}'?的信號(hào):{2}",_name,_subject.Name?,_observerState);
????????}
????????#endregion
????}
}
? 5、客戶端代碼
????????????#region?我的團(tuán)長我的團(tuán)
????????????Console.WriteLine("----------我的團(tuán)長我的團(tuán)觀察者模式示例--------");
????????????ConcreteGurad?cg?=?new?ConcreteGurad("孟煩了");
????????????ConcreteSoldier?yaoma?=?new?ConcreteSoldier(cg,?"要麻");
????????????ConcreteSoldier?sepigu?=?new?ConcreteSoldier(cg,?"蛇屁股");
????????????ConcreteSoldier?doubing?=?new?ConcreteSoldier(cg,?"豆餅");
????????????ConcreteSoldier?kangya?=?new?ConcreteSoldier(cg,?"康丫");
????????????ConcreteSoldier?milong?=?new?ConcreteSoldier(cg,?"迷龍");
????????????ConcreteSoldier?bula?=?new?ConcreteSoldier(cg,?"不辣");
????????????cg.Attach(yaoma);
????????????cg.Attach(sepigu);
????????????cg.Attach(doubing);
????????????cg.Attach(kangya);
????????????cg.Attach(milong);
????????????cg.Attach(bula);
????????????cg.FightInfo?=?"鬼子從右邊摸上來了,大家準(zhǔn)備殲滅他們.";
????????????cg.Nofity();
????????????Console.ReadKey();
????????????#endregion
程序運(yùn)行后效果如下:
?
總結(jié):
1、要點(diǎn)
?(1)抽象主體角色公開了自身的事件,可以給任意觀察者訂閱。
?(2)象觀察者角色定義了統(tǒng)一的處理行為,在C#中使用事件-代理模式的話,統(tǒng)一的處理行為并不這么重要,有的時(shí)候甚至還會(huì)限制靈活性。
?(3)觀察者往往只需要實(shí)現(xiàn)響應(yīng)方法即可。
?(4)有多個(gè)主體角色、多個(gè)觀察者角色交錯(cuò),也可以一個(gè)類型是兩個(gè)角色,主體也可以提供多個(gè)事件。從應(yīng)用上來說觀察者模式變化是非常多的。
2、優(yōu)缺點(diǎn)
? 觀察者模式的優(yōu)缺點(diǎn)
Observer模式的優(yōu)點(diǎn)是實(shí)現(xiàn)了表示層和數(shù)據(jù)邏輯層的分離,并定義了穩(wěn)定的更新消息傳遞機(jī)制,類別清晰,并抽象了更新接口,使得可以有各種各樣不同的表示層(觀察者)。
? 但是其缺點(diǎn)是每個(gè)外觀對(duì)象必須繼承這個(gè)抽像出來的接口類,這樣就造成了一些不方便,比如有一個(gè)別人寫的外觀對(duì)象,并沒有繼承該抽象類,或者接口不對(duì),我們 又希望不修改該類直接使用它。雖然可以再應(yīng)用Adapter模式來一定程度上解決這個(gè)問題,但是會(huì)造成更加復(fù)雜煩瑣的設(shè)計(jì),增加出錯(cuò)幾率。
觀察者模式的效果有以下幾個(gè)優(yōu)點(diǎn):
(1)觀察者模式在被觀察者和觀察者之間建立一個(gè)抽象的耦合。被觀察者角色所知道的只是一個(gè)具體現(xiàn)察者聚集,每一個(gè)具體現(xiàn)察者都符合一個(gè)抽象觀察者的接 口。被觀察者并不認(rèn)識(shí)任何一個(gè)具體觀察者,它只知道它們都有一個(gè)共同的接口。由于被觀察者和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層 次。
(2)觀察者模式支持廣播通信。被觀察者會(huì)向所有的登記過的觀察者發(fā)出通知。
觀察者模式有下面的一些缺點(diǎn):
(1)如果一個(gè)被觀察者對(duì)象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間。
(2)如果在被觀察者之間有循環(huán)依賴的話,被觀察者會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,導(dǎo)致系統(tǒng)崩潰。在使用觀察考模式時(shí)要特別注意這一點(diǎn)。
(3)如果對(duì)觀察者的通知是通過另外的線程進(jìn)行異步投遞的話,系統(tǒng)必須保證投遞是以自恰的方式進(jìn)行的。
(4)雖然觀察者模式可以隨時(shí)使觀察者知道所觀察的對(duì)象發(fā)生了變化,但是觀察者模式?jīng)]有相應(yīng)的機(jī)制使觀察者知道所觀察的對(duì)象是怎么發(fā)生變化的。
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/smallfa/archive/2009/11/19/1606147.html
總結(jié)
以上是生活随笔為你收集整理的设计模式记--Observer Pattern观察者模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 建立数据通道,解决IMX6边编码边解码的
- 下一篇: 正几边形可以实现无缝拼接?