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> 10 11 /// 在Observer Pattern(觀察者模式)中,此類作為所有Subject(目標)的抽象基類 12 13 /// 所有要充當Subject的類(在此事例中為"貓")都繼承於此類. 14 15 /// 我們說此類作為模型,用於規劃目標(即發布方)所產生的事件,及提供觸發 16 17 /// 事件的方法. 18 19 /// 此抽象類無抽象方法,主要是為了不能實例化該類對象,確保模式完整性. 20 21 /// 具體實施: 22 23 /// 1.聲明委托 24 25 /// 2.聲明委托類型事件 26 27 /// 3.提供觸發事件的方法 28 29 /// </summary> 30 31 public abstract class ModelBase 32 33 { 34 35 public ModelBase() 36 37 { 38 39 } 40 41 /**//// <summary> 42 43 /// 聲明一個委托,用於代理一系列"無返回"及"不帶參"的自定義方法 44 45 /// </summary> 46 47 public delegate void SubEventHandler(); 48 49 /**//// <summary> 50 51 /// 聲明一個綁定於上行所定義的委托的事件 52 53 /// </summary> 54 55 public event SubEventHandler SubEvent; 56 57 58 59 /**//// <summary> 60 61 /// 封裝了觸發事件的方法 62 63 /// 主要為了規範化及安全性,除觀察者基類外,其派生類不直接觸發委托事件 64 65 /// </summary> 66 67 protected void Notify() 68 69 { 70 71 //提高執行效率及安全性 72 73 if(this.SubEvent!=null) 74 75 this.SubEvent(); 76 77 78 79 } 80 81 } 82 83}
2. 建立觀察者基類(單行為,多行為)
//--------------------單行為---------------------
1using System; 2 3 4 5namespace DelegateEvent 6 7{ 8 9 /**//// <summary> 10 11 /// 在Observer Pattern(觀察者模式)中,此類作為所有Observer(觀察者)的抽象基類 12 13 /// 所有要充當觀察者的類(在此事例中為"老鼠"和"人")都繼承於此類. 14 15 /// 我們說此類作為觀察者基類,用於規劃所有觀察者(即訂閱方)訂閱行為. 16 17 /// 在此事例中,規劃了針對目標基類(ModelBase)中聲明的"無參無返回"委托的一個 18 19 /// 方法(Response),並於構造該觀察者時將其註冊於具體目標(參數傳遞)的委托事件中. 20 21 /// 具體實施過程: 22 23 /// 1.指定觀察者所觀察的對象(即發布方).(通過構造器傳遞) 24 25 /// 2.規劃觀察者自身需要作出響應方法列表 26 27 /// 3.註冊需要委托執行的方法.(通過構造器實現) 28 29 /// </summary> 30 31 public abstract class Observer 32 33 { 34 35 /**//// <summary> 36 37 /// 構造時通過傳入模型對象,把觀察者與模型關聯,並完成訂閱. 38 39 /// 在此確定需要觀察的模型對象. 40 41 /// </summary> 42 43 /// <param name="childModel">需要觀察的對象</param> 44 45 public Observer(ModelBase childModel) 46 47 { 48 49 //訂閱 50 51 //把觀察者行為(這裏是Response)註冊於委托事件 52 53 childModel.SubEvent+=new ModelBase.SubEventHandler(Response); 54 55 } 56 57 58 59 /**//// <summary> 60 61 /// 規劃了觀察者的一種行為(方法),所有派生於該觀察者基類的具體觀察者都 62 63 /// 通過覆蓋該方法來實現作出響應的行為. 64 65 /// </summary> 66 67 public abstract void Response(); 68 69 } 70 71} 72 73
//-------------------多行為-------------------
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// 定義了另一個觀察者基類.該觀察者類型擁有兩個響應行為. 10 11 /// 並在構造時將響應行為註冊於委托事件. 12 13 /// (具體描述請參照另一觀察者基類Observer) 14 15 /// </summary> 16 17 public abstract class Observer2 18 19 { 20 21 /**//// <summary> 22 23 /// 構造時通過傳入模型對象,把觀察者與模型關聯,並完成訂閱. 24 25 /// 在此確定需要觀察的模型對象. 26 27 /// </summary> 28 29 /// <param name="childModel">需要觀察的對象</param> 30 31 public Observer2(ModelBase childModel) 32 33 { 34 35 //訂閱 36 37 //把觀察者行為(這裏是Response和Response2)註冊於委托事件 38 39 childModel.SubEvent+=new ModelBase.SubEventHandler(Response); 40 41 childModel.SubEvent+=new ModelBase.SubEventHandler(Response2); 42 43 44 45 } 46 47 /**//// <summary> 48 49 /// 規劃了觀察者的二種行為(方法),所有派生於該觀察者基類的具體觀察者都 50 51 /// 通過覆蓋該方法來實現作出響應的行為. 52 53 /// </summary> 54 55 public abstract void Response(); 56 57 public abstract void Response2(); 58 59 } 60 61}
3.
建立具體目標
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// 此類為觀察者模式中的具體目標(即具體發布方),其繼承於模型. 10 11 /// 其中包含(調用)了在模型中被封裝好的觸發委托事件的方法. 12 13 /// </summary> 14 15 public class Cat:ModelBase 16 17 { 18 19 public Cat() 20 21 { 22 23 } 24 25 /**//// <summary> 26 27 /// 定義了貓的一種行為----大叫 28 29 /// </summary> 30 31 public void Cry() 32 33 { 34 35 System.Console.WriteLine("Cat Cry.."); 36 37 //調用了觸發委托事件的方法. 38 39 //通知委托開始執行觀察者已訂閱的方法. 40 41 this.Notify(); 42 43 } 44 45 } 46 47} 48
4. 建立具體觀察者
//--------------具體觀察者(老鼠)-------------
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// 此類為觀察者模式中的具體觀察者(即具體發布方),其繼承於觀察者基類. 10 11 /// 其中覆蓋了觀察者基類規劃好的方法,實現了響應的具體行為. 12 13 /// </summary> 14 15 public class Mouse:Observer 16 17 { 18 19 /**//// <summary> 20 21 /// 觀察者可以擁有自己的成員(字段或者方法). 22 23 /// 在此事例中增加了"老鼠的名字" 24 25 /// </summary> 26 27 private string name; 28 29 /**//// <summary> 30 31 /// 構造時確定觀察者所需要觀察的對象(具體目標),並傳遞給觀察者基類構造器, 32 33 /// 實現響應行為(方法)的訂閱.另外,為觀察者實例初始化成員. 34 35 /// </summary> 36 37 /// <param name="name">老鼠的名字</param> 38 39 /// <param name="childModel"> 40 41 /// 需要觀察的對象(發布方). 42 43 /// 此處用模型基類來傳遞,是為了兼容所有派生於此模型的觀察者,從而提高擴展性. 44 45 /// </param> 46 47 public Mouse(string name,ModelBase childModel):base(childModel) 48 49 { 50 51 //初始化字段(老鼠的名字) 52 53 this.name=name; 54 55 } 56 57 /**//// <summary> 58 59 /// 覆蓋了該類觀察者需要作出的具體響應行為. 60 61 /// 此行為已在觀察者基類中註冊於委托事件,由委托事件調度執行,不需要直接調用. 62 63 /// </summary> 64 65 public override void Response() 66 67 { 68 69 //具體響應內容 70 71 System.Console.WriteLine(this.name+"開始逃跑"); 72 73 } 74 75 76 77 } 78 79} 80 81
//----------------具體觀察者(主人)----------------
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// 此類為觀察者模式中的具體觀察者(即具體發布方),其繼承於觀察者基類. 10 11 /// 其中覆蓋了觀察者基類規劃好的方法,實現了響應的具體行為. 12 13 /// </summary> 14 15 public class Master:Observer 16 17 { 18 19 /**//// <summary> 20 21 /// 構造時確定觀察者所需要觀察的對象(具體目標),並傳遞給觀察者基類構造器, 22 23 /// 實現響應行為(方法)的訂閱. 24 25 /// </summary> 26 27 public Master(ModelBase childModel):base(childModel) 28 29 { 30 31 } 32 33 34 35 /**//// <summary> 36 37 /// 覆蓋了該類觀察者需要作出的具體響應行為. 38 39 /// 此行為已在觀察者基類中註冊於委托事件,由委托事件調度執行,不需要直接調用. 40 41 /// </summary> 42 43 public override void Response() 44 45 { 46 47 System.Console.WriteLine("主人醒來"); 48 49 } 50 51 } 52 53} 54 55
//-------------------具體觀察者(寶寶)-------------
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// 此類為觀察者模式中的具體觀察者(即具體發布方),其繼承了訂閱了2個響應行為的 10 11 /// 觀察者基類. 12 13 /// 其中覆蓋了觀察者基類規劃好的二個方法,實現了響應的具體行為. 14 15 /// </summary> 16 17 public class Master2:Observer2 18 19 { 20 21 /**//// <summary> 22 23 /// 構造時確定觀察者所需要觀察的對象(具體目標),並傳遞給觀察者基類構造器, 24 25 /// 實現響應行為(方法)的訂閱. 26 27 /// </summary> 28 29 public Master2(ModelBase childBase):base(childBase) 30 31 { 32 33 } 34 35 36 37 /**//// <summary> 38 39 /// 覆蓋了該類觀察者需要作出的具體響應行為. 40 41 /// 此行為已在觀察者基類中註冊於委托事件,由委托事件調度執行,不需要直接調用. 42 43 /// </summary> 44 45 public override void Response() 46 47 { 48 49 Console.WriteLine("baby醒來。。。。"); 50 51 52 53 } 54 55 /**//// <summary> 56 57 /// 覆蓋了該類觀察者需要作出的另一個響應行為. 58 59 /// </summary> 60 61 public override void Response2() 62 63 { 64 65 Console.WriteLine("開始哭鬧。。。。。"); 66 67 } 68 69 } 70 71} 72 73
5. 運行測試
1using System; 2 3 namespace DelegateEvent 4 5{ 6 7 /**//// <summary> 8 9 /// Observer Pattern(觀察者模式)事例分析 10 11 /// 12 13 /// 題目:貓大叫,兩只老鼠開始逃跑,主人醒來,寶寶也醒來了並且哭了起來. 14 15 /// 16 17 /// 關於目標(發布方): 18 19 /// 在此事例中,只有一個目標對象(發布方)貓,因為其他全部實體的行為都是 20 21 /// 響應它的"大叫"所執行的.貓是主動方,它的大叫引起一系列的連鎖反應. 22 23 /// 24 25 /// 關於觀察者(訂閱方): 26 27 /// 至於此事例的中的觀察者分別有兩大類,一類是聽到貓大叫後只作出一種 28 29 /// 反應的觀察者(老鼠,主人),另一類是聽到錨大叫後會作出兩種響應的觀察者( 30 31 /// 寶寶).所以觀察者分別需要派生於兩個不同的觀察者基類. 32 33 /// </summary> 34 35 public class SubMain 36 37 { 38 39 public SubMain() 40 41 { 42 43 44 45 } 46 47 public static void Main() 48 49 { 50 51 //聲明並實例化一個目標(即發布方)對象----貓 52 53 Cat myCat=new Cat(); 54 55 //聲明並實例化一個Mouse類型的觀察者對象--名叫mouse1的老鼠.並把那只貓作為它所要觀察的對象. 56 57 Mouse myMouse1=new Mouse("mouse1",myCat); 58 59 //類似地生成另一只名叫mouse2的老鼠(觀察者),把同一只貓作為它的觀察的對象. 60 61 Mouse myMouse2=new Mouse("mouse2",myCat); 62 63 //聲明並實例化一個Master類型的觀察者--主人,並同時把那只貓也作為他的觀察對象. 64 65 Master myMaster=new Master(myCat); 66 67 //聲明並實例化一個Master2類型的觀察者--寶寶,同時把那只貓也 68 69 Master2 myLittleMaster=new Master2(myCat); 70 71 72 73 //貓大叫,並觸發了委托事件,從而開始按順序調用觀察者已訂閱的方法. 74 75 myCat.Cry(); 76 77 78 79 Console.Read(); 80 81 } 82 83 } 84 85} 86
c#設計模式-觀察者模式