1、觀察者模式編程
目的 概述 原理設計模式
2、 C#中的觀察者模式安全
概述 模型與觀察者基類 優勢架構
3、 事例框架
題目:貓大叫,兩隻老鼠開始逃跑,主人醒來,寶寶也醒來了而且哭了起來ide
解決方案: 1. 創建模型(目標基類) 2. 創建觀察者基類(單行爲,多行爲) 3. 創建具體目標 4. 創建具體觀察者 5. 運行測試測試
1、 觀察者模式this
目的spa
咱們都知道解決一個問題有N種解決方式,但在面向對象的設計中如何能作到「高內聚,低耦合」,設計可重用的對象纔是咱們追求的。在設計過程當中,咱們常常會接觸到一種狀況:一個對象的行爲引起其它多個對象相應的行爲。這時咱們即可以經過觀察者模式的設計思想來設計對象模型。設計
概述
觀察者模式(Observer Pattern)是設計模式中行爲模式的一種,它解決了上述具備一對多依賴關係的對象的重用問題。此模式的參與者分爲兩大類,一類是被觀察的目標,另外一類是觀察該目標的觀察者們。正由於該模式是基於「一對多」的關係,因此該模式通常是應用於由一個目標對象和N個觀察者對象組成(固然也能夠擴展爲有多個目標對象,但咱們如今只討論前者)的場合。當目標對象的狀態發生改變或作出某種行爲時,正在觀察該目標對象的觀察者們將自動地、連鎖地做出相應的響應行爲。
原理
咱們能夠把觀察目標理解爲主動方、發佈方、主體等;把觀察者理解爲被動方、訂閱方、觀察器等。目標是整個行爲鏈的源頭,其它觀察者都依賴於它的變化而做出響應。爲了實現低耦合,咱們不能使用「直接調用」的方式而須要利用「訂閱(清單)-通知」的機制去完成設計。通俗地說就是觀察者向目標「訂閱」它的改變,而目標發生改變後就「通知」全部已經「訂閱」了它的改變的觀察者,從而執行「訂閱」的內容。這種機制的好處在於下降耦合度,分工明確,目標只負責在自身狀態發生改變或作出某種行爲時向自身的訂閱清單發出「通知」,而不是直接調用觀察者的行爲(方法);觀察者只負責向目標「訂閱」它的變化,以及定義自身在收到目標「通知」後所須要作出的具體行爲(也就是訂閱的內容)。就像咱們向出版社訂閱報刊同樣,出版社有新一期報刊發行時並非直接跟每位訂閱者聯繫,而是「通知」訂閱者名單按順序給每位訂閱者發送所訂報刊。
2、 C#中的觀察者模式
概述
每種編程架構及程序語言,對觀察者模式都有不通的具體實現。在.NET框架中,C#語言使用委託以及事件,能夠很好的實現觀察者模式。委託至關於「訂閱清單」的角色,當目標中關聯了該委託的事件被觸發時,則委託將自動按序執行觀察者註冊於委託中的方法。
模型與觀察者基類
咱們把觀察者模式的參與者都描述爲派生自模型及觀察者二個抽象基類的類。模型規劃了事件,而觀察者則規劃了訂閱及行爲。
模型須要作的只是聲明委託以及聲明委託類型的事件。固然,還能夠附加上封裝了觸發委託事件的方法。全部派生自模型的類都是具體目標,它們所要作的只是在適當的場合觸發事件。(即發出「通知」)。
在觀察者基類中,咱們經過構造器將抽象的響應方法註冊(訂閱)於委託事件中。全部派生自觀察者基類的類都是具體觀察者。由於訂閱行爲已經在抽象基類完成,具體觀察者須要作的只是經過覆蓋觀察者基類的方法去定義具體須要響應的行爲,和經過構造器把須要觀察的具體目標傳遞給基類構造器。
優勢
經過對模型與觀察者基類的分析可知,委託與事件的機制幾乎消除了這兩個模塊之間的耦合,靈活性提升了不少。若是須要增長觀察者,則只須要覆蓋基類抽象方法及把觀察目標傳遞給基類。
3、 事例
題目:貓大叫,兩隻老鼠開始逃跑,主人醒來,寶寶也醒來了而且哭了起來.
解決方案:
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