標籤:c#語言 繼承 有一個 過程 清單 模型 目的 情況 變化
Observer 與 Subject 互為耦合,但是這種耦合的雙方都依賴於抽象,而不依賴於具體。
一、觀察者模式
目的 概述 原理
二、 C#中的觀察者模式
概述 模型與觀察者基類 優點
三、 案例
題目:貓大叫,兩隻老鼠開始逃跑,主人醒來,寶寶也醒來了並且哭了起來
解決方案: 1. 建立模型(目標基類) 2. 建立觀察者基類(單行為,多行為) 3. 建立具體目標 4. 建立具體觀察者 5. 運行測試
一、 觀察者模式
目的
我們都知道解決一個問題有N種解決方式,但在物件導向的設計中如何能做到“高內聚,低耦合”,設計可重用的對象才是我們追求的。在設計過程中,我們經常會接觸到一種情況:一個對象的行為引發其它多個對象相應的行為。這時我們便可以通過觀察者模式的設計思想來設計物件模型。
概述
觀察者模式(Observer Pattern)是設計模式中行為模式的一種,它解決了上述具有一對多依賴關係的對象的重用問題。此模式的參與者分為兩大類,一類是被觀察的目標,另一類是觀察該目標的觀察者們。正因為該模式是基於“一對多”的關係,所以該模式一般是應用於由一個目標對象和N個觀察者對象組成(當然也可以擴充為有多個目標對象,但我們現在只討論前者)的場合。當目標對象的狀態發生改變或做出某種行為時,正在觀察該目標對象的觀察者們將自動地、連鎖地作出相應的響應行為。
原理
我們可以把觀察目標理解為主動方、發布方、主體等;把觀察者理解為被動方、訂閱者、觀察器等。目標是整個行為鏈的源頭,其它觀察者都依賴於它的變化而作出響應。為了實現低耦合,我們不能使用“直接調用”的方式而需要利用“訂閱(清單)-通知”的機制去完成設計。通俗地說就是觀察者向目標“訂閱”它的改變,而目標發生改變後就“通知”所有已經“訂閱”了它的改變的觀察者,從而執行“訂閱”的內容。這種機制的好處在於降低耦合度,分工明確,目標只負責在自身狀態發生改變或做出某種行為時向自身的訂閱清單發出“通知”,而不是直接調用觀察者的行為(方法);觀察者只負責向目標“訂閱”它的變化,以及定義自身在收到目標“通知”後所需要做出的具體行為(也就是訂閱的內容)。就像我們向出版社訂閱報刊一樣,出版社有新一期報刊發行時並不是直接跟每位訂閱者聯絡,而是“通知”訂閱者名單按順序給每位訂閱者發送所訂報刊。
二、 C#中的觀察者模式
概述
每種編程架構及程式語言,對觀察者模式都有不通的具體實現。在.NET架構中,C#語言使用委託以及事件,可以很好的實現觀察者模式。委託相當於“訂閱清單”的角色,當目標中關聯了該委託的事件被觸發時,則委託將自動按序執行觀察者註冊於委託中的方法。
模型與觀察者基類
我們把觀察者模式的參與者都描述為派生自模型及觀察者二個抽象基類的類。模型規划了事件,而觀察者則規划了訂閱及行為。
模型需要做的只是聲明委託以及聲明委託類型的事件。當然,還可以附加上封裝了觸發委託事件的方法。所有派生自模型的類都是具體目標,它們所要做的只是在適當的場合觸發事件。(即發出“通知”)。
在觀察者基類中,我們通過構造器將抽象的回應程式法註冊(訂閱)於委託事件中。所有派生自觀察者基類的類都是具體觀察者。因為訂閱行為已經在抽象基類完成,具體觀察者需要做的只是通過覆蓋觀察者基類的方法去定義具體需要響應的行為,和通過構造器把需要觀察的具體目標傳遞給基類構造器。
優點
通過對模型與觀察者基類的分析可知,委託與事件的機制幾乎消除了這兩個模組之間的耦合,靈活性提高了很多。如果需要增加觀察者,則只需要覆蓋基類抽象方法及把觀察目標傳遞給基類。
三、 案例
題目:貓大叫,兩隻老鼠開始逃跑,主人醒來,寶寶也醒來了並且哭了起來.
解決方案:
1. 建立模型(目標基類)
1using System; 2 3 4 5namespace DelegateEvent 6 7{ 8 9 /**//// <summary>1011 /// 在Observer Pattern(觀察者模式)中,此類作為所有Subject(目標)的抽象基類1213 /// 所有要充當Subject的類(在此案例中為"貓")都繼承於此類.1415 /// 我們說此類作為模型,用於規劃目標(即發布方)所產生的事件,及提供觸發1617 /// 事件的方法.1819 /// 此抽象類別無抽象方法,主要是為了不能執行個體化該類對象,確保模式完整性.2021 /// 具體實施:2223 /// 1.聲明委託2425 /// 2.聲明委託類型事件2627 /// 3.提供觸發事件的方法2829 /// </summary>3031 public abstract class ModelBase3233 {3435 public ModelBase()3637 {3839 }4041 /**//// <summary>4243 /// 聲明一個委託,用於代理一系列"無返回"及"不帶參"的自訂方法4445 /// </summary>4647 public delegate void SubEventHandler(); 4849 /**//// <summary>5051 /// 聲明一個綁定於上行所定義的委託的事件5253 /// </summary>5455 public event SubEventHandler SubEvent;5657 5859 /**//// <summary>6061 /// 封裝了觸發事件的方法6263 /// 主要為了正常化及安全性,除觀察者基類外,其衍生類別不直接觸發委託事件6465 /// </summary>6667 protected void Notify()6869 {7071 //提高執行效率及安全性7273 if(this.SubEvent!=null)7475 this.SubEvent();7677 7879 }8081 }8283}
2. 建立觀察者基類(單行為,多行為)
//--------------------單行為---------------------
1using System; 2 3 4 5namespace DelegateEvent 6 7{ 8 9 /**//// <summary>1011 /// 在Observer Pattern(觀察者模式)中,此類作為所有Observer(觀察者)的抽象基類1213 /// 所有要充當觀察者的類(在此案例中為"老鼠"和"人")都繼承於此類.1415 /// 我們說此類作為觀察者基類,用於規劃所有觀察者(即訂閱者)訂閱行為.1617 /// 在此案例中,規划了針對目標基類(ModelBase)中聲明的"無參無返回"委託的一個1819 /// 方法(Response),並於構造該觀察者時將其註冊於具體目標(參數傳遞)的委託事件中.2021 /// 具體實施過程:2223 /// 1.指定觀察者所觀察的對象(即發布方).(通過構造器傳遞)2425 /// 2.規劃觀察者自身需要作出回應程式法列表2627 /// 3.註冊需要委託執行的方法.(通過構造器實現)2829 /// </summary>3031 public abstract class Observer3233 {3435 /**//// <summary>3637 /// 構造時通過傳入模型對象,把觀察者與模型關聯,並完成訂閱.3839 /// 在此確定需要觀察的模型對象.4041 /// </summary>4243 /// <param name="childModel">需要觀察的對象</param>4445 public Observer(ModelBase childModel)4647 {4849 //訂閱5051 //把觀察者行為(這裡是Response)註冊於委託事件5253 childModel.SubEvent+=new ModelBase.SubEventHandler(Response);5455 }5657 5859 /**//// <summary>6061 /// 規划了觀察者的一種行為(方法),所有派生於該觀察者基類的具體觀察者都6263 /// 通過覆蓋該方法來實現作出響應的行為.6465 /// </summary>6667 public abstract void Response();6869 }7071}7273
//-------------------多行為-------------------
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// 定義了另一個觀察者基類.該觀察者類型擁有兩個響應行為.1011 /// 並在構造時將響應行為註冊於委託事件.1213 /// (具體描述請參照另一觀察者基類Observer)1415 /// </summary>1617 public abstract class Observer21819 {2021 /**//// <summary>2223 /// 構造時通過傳入模型對象,把觀察者與模型關聯,並完成訂閱.2425 /// 在此確定需要觀察的模型對象.2627 /// </summary>2829 /// <param name="childModel">需要觀察的對象</param>3031 public Observer2(ModelBase childModel)3233 {3435 //訂閱3637 //把觀察者行為(這裡是Response和Response2)註冊於委託事件3839 childModel.SubEvent+=new ModelBase.SubEventHandler(Response);4041 childModel.SubEvent+=new ModelBase.SubEventHandler(Response2);4243 4445 }4647 /**//// <summary>4849 /// 規划了觀察者的二種行為(方法),所有派生於該觀察者基類的具體觀察者都5051 /// 通過覆蓋該方法來實現作出響應的行為.5253 /// </summary>5455 public abstract void Response();5657 public abstract void Response2();5859 }6061}
3.
建立具體目標
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// 此類為觀察者模式中的具體目標(即具體發布方),其繼承於模型.1011 /// 其中包含(調用)了在模型中被封裝好的觸發委託事件的方法.1213 /// </summary>1415 public class Cat:ModelBase1617 {1819 public Cat()2021 {2223 }2425 /**//// <summary>2627 /// 定義了貓的一種行為----大叫2829 /// </summary>3031 public void Cry()3233 {3435 System.Console.WriteLine("Cat Cry..");3637 //調用了觸發委託事件的方法.3839 //通知委託開始執行觀察者已訂閱的方法.4041 this.Notify(); 4243 }4445 }4647}48
4. 建立具體觀察者
//--------------具體觀察者(老鼠)-------------
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// 此類為觀察者模式中的具體觀察者(即具體發布方),其繼承於觀察者基類.1011 /// 其中覆蓋了觀察者基類規劃好的方法,實現了響應的具體行為.1213 /// </summary>1415 public class Mouse:Observer1617 {1819 /**//// <summary>2021 /// 觀察者可以擁有自己的成員(欄位或者方法).2223 /// 在此案例中增加了"老鼠的名字"2425 /// </summary>2627 private string name;2829 /**//// <summary>3031 /// 構造時確定觀察者所需要觀察的對象(具體目標),並傳遞給觀察者基類構造器,3233 /// 實現響應行為(方法)的訂閱.另外,為觀察者執行個體初始化成員.3435 /// </summary>3637 /// <param name="name">老鼠的名字</param>3839 /// <param name="childModel">4041 /// 需要觀察的對象(發布方).4243 /// 此處用模型基類來傳遞,是為了相容所有派生於此模型的觀察者,從而提高擴充性.4445 /// </param>4647 public Mouse(string name,ModelBase childModel):base(childModel)4849 {5051 //初始化欄位(老鼠的名字)5253 this.name=name; 5455 }5657 /**//// <summary>5859 /// 覆蓋了該類觀察者需要作出的具體響應行為.6061 /// 此行為已在觀察者基類中註冊於委託事件,由委託事件調度執行,不需要直接調用.6263 /// </summary>6465 public override void Response()6667 {6869 //具體響應內容7071 System.Console.WriteLine(this.name+"開始逃跑");7273 }7475 7677 }7879}8081
//----------------具體觀察者(主人)----------------
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// 此類為觀察者模式中的具體觀察者(即具體發布方),其繼承於觀察者基類.1011 /// 其中覆蓋了觀察者基類規劃好的方法,實現了響應的具體行為.1213 /// </summary>1415 public class Master:Observer1617 {1819 /**//// <summary>2021 /// 構造時確定觀察者所需要觀察的對象(具體目標),並傳遞給觀察者基類構造器,2223 /// 實現響應行為(方法)的訂閱.2425 /// </summary>2627 public Master(ModelBase childModel):base(childModel)2829 {3031 }3233 3435 /**//// <summary>3637 /// 覆蓋了該類觀察者需要作出的具體響應行為.3839 /// 此行為已在觀察者基類中註冊於委託事件,由委託事件調度執行,不需要直接調用.4041 /// </summary>4243 public override void Response()4445 {4647 System.Console.WriteLine("主人醒來");4849 }5051 }5253}5455
//-------------------具體觀察者(寶寶)-------------
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// 此類為觀察者模式中的具體觀察者(即具體發布方),其繼承了訂閱了2個響應行為的1011 /// 觀察者基類.1213 /// 其中覆蓋了觀察者基類規劃好的二個方法,實現了響應的具體行為.1415 /// </summary>1617 public class Master2:Observer21819 {2021 /**//// <summary>2223 /// 構造時確定觀察者所需要觀察的對象(具體目標),並傳遞給觀察者基類構造器,2425 /// 實現響應行為(方法)的訂閱.2627 /// </summary>2829 public Master2(ModelBase childBase):base(childBase)3031 {3233 }3435 3637 /**//// <summary>3839 /// 覆蓋了該類觀察者需要作出的具體響應行為.4041 /// 此行為已在觀察者基類中註冊於委託事件,由委託事件調度執行,不需要直接調用.4243 /// </summary>4445 public override void Response()4647 {4849 Console.WriteLine("baby醒來。。。。");5051 5253 }5455 /**//// <summary>5657 /// 覆蓋了該類觀察者需要作出的另一個響應行為.5859 /// </summary>6061 public override void Response2()6263 {6465 Console.WriteLine("開始哭鬧。。。。。");6667 }6869 }7071}7273
5. 運行測試
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// Observer Pattern(觀察者模式)案例分析1011 /// 1213 /// 題目:貓大叫,兩隻老鼠開始逃跑,主人醒來,寶寶也醒來了並且哭了起來.1415 /// 1617 /// 關於目標(發布方):1819 /// 在此案例中,只有一個目標對象(發布方)貓,因為其他全部實體的行為都是2021 /// 響應它的"大叫"所執行的.貓是主動方,它的大叫引起一系列的連鎖反應.2223 /// 2425 /// 關於觀察者(訂閱者):2627 /// 至於此案例的中的觀察者分別有兩大類,一類是聽到貓大叫後只作出一種2829 /// 反應的觀察者(老鼠,主人),另一類是聽到錨大叫後會作出兩種響應的觀察者(3031 /// 寶寶).所以觀察者分別需要派生於兩個不同的觀察者基類.3233 /// </summary>3435 public class SubMain3637 {3839 public SubMain()4041 {4243 4445 }4647 public static void Main()4849 {5051 //聲明並執行個體化一個目標(即發布方)對象----貓5253 Cat myCat=new Cat();5455 //聲明並執行個體化一個Mouse類型的觀察者對象--名叫mouse1的老鼠.並把那隻貓作為它所要觀察的對象.5657 Mouse myMouse1=new Mouse("mouse1",myCat);5859 //類似地產生另一隻名叫mouse2的老鼠(觀察者),把同一隻貓作為它的觀察的對象.6061 Mouse myMouse2=new Mouse("mouse2",myCat);6263 //聲明並執行個體化一個Master類型的觀察者--主人,並同時把那隻貓也作為他的觀察對象.6465 Master myMaster=new Master(myCat);6667 //聲明並執行個體化一個Master2類型的觀察者--寶寶,同時把那隻貓也6869 Master2 myLittleMaster=new Master2(myCat);7071 7273 //貓大叫,並觸發了委託事件,從而開始按順序調用觀察者已訂閱的方法.7475 myCat.Cry();7677 7879 Console.Read();8081 }8283 }8485}86
c#設計模式-觀察者模式