C# 观察者模式 以及 delegate 和 event
觀察者模式
這里面綜合了幾本書的資料.
需求
有這么個項目:?
需求是這樣的:
一個氣象站, 有三個傳感器(溫度, 濕度, 氣壓), 有一個WeatherData對象, 它能從氣象站獲得這三個數據. 還有三種設備, 可以按要求展示氣象站的最新數據.
WeatherData的結構如下:
有3個get方法, 分別獲取最新的氣溫, 濕度和氣壓. 還有一個measurementsChanged()方法, 當任一傳感器有變化的時候, 這個方法都會被調用.
總結一下項目的需求:
WeatherData類有三個get方法可以獲取溫度, 濕度和氣壓
如果任何一個數據發生變化, 那么measureChanged()方法就會被調用
我們需要實現這三種顯示設備:
當前天氣
數據統計
天氣預測
系統必須可以擴展, 其他開發者可以創建自定義展示設備.
初版代碼
這個地方有個"錯誤", xxxDisplay都是具體的實現, 而編程規則要求是應該對接口編程而不是對實現編程.
那么什么是觀察者模式?
舉一個例子:
報社發行報紙
你訂閱報紙, 一旦有新一期的報紙發行, 新報紙就會送到你家里, 只要你一直訂閱, 你就一直會收到新報紙
你不再訂閱報紙的時候, 就收不到以后的新報紙了
報社運營的時候, 一直會有人去訂閱或者取消訂閱報紙.
發布者 + 訂閱者 = 觀察者模式
Publishers + Subscribers = Observer Pattern
在觀察者模式里, 我們把報社叫做被觀察對象(Subject), 把訂閱者叫做觀察者(Observers)
觀察者模式是這樣操作的:
??
觀察者模式的定義就是:
一個目標物件管理所有相依于它的觀察者物件,并且在它本身的狀態改變時主動發出通知。
類圖如下:
?
談一下松耦合
當兩個對象是松耦合的時候, 他們可以進行交互, 但是卻幾乎不了解對方.
觀察者模式下的被觀察者(Subject)和觀察者(Observers)就是松耦合設計的對象. 這是因為:
被觀察者(Subject)只知道觀察者實現了某個接口
可以隨時添加觀察者
添加新類型觀察者的時候不需要修改被觀察者
可以復用觀察者或者被觀察者
如果被觀察者或觀察者發生變化了, 那么這些變化不會影響到對方.
一個設計原則:
交互的對象之間應盡量設計成松耦合的.?Strive for loosely coupled designs between objects that interact.
松耦合設計可以讓我們設計出這樣的系統: 因為對象之間的相互依存減小了, 所以系統可以輕松處理變化.
重新設計:
代碼:
OK, 上面是書中的內容, C#7.0里面對觀察者模式是怎么實現的呢?
先只談下面這個:
Event
談到Event, 就得把delegate先細說一下
Delegate 委托
一個委托類型定義了某種類型的方法(方法的返回類型和參數類型), 然后這個委托的實例可以調用這些方法.
例如:
delegate int Transformer (int x);這個委托就和返回類型是int, 參數是一個int的方法兼容.
例如:
static int Square (int x) { return x * x };// 或static int Square (int x) => x * x;?
把一個方法賦值給委托變量的時候就創建了一個委托的實例:
Transformer t = Square;?
然后就可以像方法一樣進行調用:
int answer = t(3); // 9?
所以說一個委托的實例就是調用者的委托: 調用者調用委托, 然后委托調用目標方法, 這樣就把調用者和目標方法解耦了.
其中:
Transformer t = Square;// 是下面的簡寫Transformer t = new Transformer(Square);?
t(3)// 是下面的簡寫t.Invoke(3)?
多播委托
一個委托實例可以引用多個目標方法. 使用+=操作符.
SomeDelegate d = Method1; d += Method2;// 第二行相當于:d = d + Method2;?
調用d的時候就會調用Method1和Method2兩個方法.
委托方法的調用順序和它們被添加的順序是一樣的.
使用-=操作符來移除目標方法:
d -= Method1;?
這時調用d后只會執行Method2了.
注意: 委托是不可變的 +=/-=實際上是創建了新的委托.
多播委托返回類型
如果多播委托有返回值(非void), 那么調用者只會獲得最后一個被調用方法的返回值.
委托也可以使用泛型:
public delegate T Transformer<T> (T arg);?
Func 和 Action
記住Func有返回值, Action沒有就行.
?
Event
使用委托的時候, 通常會有兩個角色出現: 廣播者(被觀察者)和訂閱者(觀察者) [觀察者模式]
廣播者包含一個委托field, 廣播者決定何時廣播, 它通過調用委托進行廣播.
訂閱者就是方法的目標接收者.訂閱者可以決定何時開始和結束監聽, 是通過在廣播者的委托上使用+=和-=操作符來實現的.
訂閱者之間互相不了解, 不干擾.
event就是為上述模型所存在的, 它只把上述模型所必須的功能從委托里暴露出來. 它的主要目的就是防止訂閱者之間相互干擾.
最簡單聲明event的方法就是在委托成員前面加上event關鍵字:
public delegate void SomeChangedHandler(decimal x);
public class Broadcaster
{
? ? public event SomeChangedHandler handler;
}
在Broadcaster類里面的代碼, 可以把handler作為委托一樣來用.
在Broadcaster類外邊, 只能對這個event執行+=和-=操作.
?
Event 模式/ 觀察者模式
這種模式在.net core里首先需要EventArgs.
EventArgs是一個基類, 它可以為event傳遞信息.
可以創造它的子類來傳遞自定義參數:
public class FallsIllEventArgs : EventArgs
? ? {
? ? ? ? public readonly string Address;
? ? ? ? public FallsIllEventArgs(string address)
? ? ? ? {
? ? ? ? ? ? this.Address = address;
? ? ? ? }
? ? }
然后就需要給這個event定義一個委托了, 這有三條規則:
返回類型必須是void
需要有兩個參數, 第一個是object, 第二個是EventArgs的子類. 第一個參數代表著廣播者, 第二個參數包含額外的需要傳遞的信息.
名稱必須以EventHandler結束.
.net core定義了System.EventHandler<>, 它滿足這些要求.
public event EventHandler<FallsIllEventArgs> FallsIll;?
最后, 需要寫一個 protected virtual 方法可以觸發event. 方法的名稱必須和event匹配: 以On開頭, 接受EventArgs類型的參數:
public void OnFallsIll(){FallsIll?.Invoke(this, new FallsIllEventArgs("China Beijing"));}?注意: 預定義的非泛型的EventHandler委托可以在沒有數據需要傳輸的時候使用, 調用的時候可以使用EventArgs.Empty來避免不必要的初始化EventArgs.
?
用.net core 實現觀察者模式的代碼:
Person.cs
using System;
namespace ObserverPattern
{
? ? public class Person
? ? {
? ? ? ? public event EventHandler<FallsIllEventArgs> FallsIll;
? ? ? ? public void OnFallsIll()
? ? ? ? {
? ? ? ? ? ? FallsIll?.Invoke(this, new FallsIllEventArgs("China Beijing"));
? ? ? ? }
? ? }
}
FallsIllEventArgs.cs:
using System;
namespace ObserverPattern
{
? ? public class FallsIllEventArgs : EventArgs
? ? {
? ? ? ? public readonly string Address;
? ? ? ? public FallsIllEventArgs(string address)
? ? ? ? {
? ? ? ? ? ? this.Address = address;
? ? ? ? }
? ? }
}
Program.cs:
using System;
namespace ObserverPattern
{
? ? class Program
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? var person = new Person();
? ? ? ? ? ? person.FallsIll += OnFallsIll;
? ? ? ? ? ? person.OnFallsIll();
? ? ? ? ? ? person.FallsIll -= OnFallsIll;
? ? ? ? }
? ? ? ? private static void OnFallsIll(object sender, FallsIllEventArgs eventArgs)
? ? ? ? {
? ? ? ? ? ? Console.WriteLine($"A doctor has been called to {eventArgs.Address}");
? ? ? ? }
? ? }
}
原文地址?https://www.cnblogs.com/cgzl/p/8688476.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結
以上是生活随笔為你收集整理的C# 观察者模式 以及 delegate 和 event的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Slickflow.NET 开源工作流引
- 下一篇: RabbitMQ教程C#版 - 工作队列