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