1. 程式人生 > >.net core 常見設計模式-IChangeToken

.net core 常見設計模式-IChangeToken

場景

一個物件A,希望它的某些狀態在發生改變時通知到B(或C、D),
常見的做法是在A中定義一個事件(或直接用委託),當狀態改變時A去觸發這個事件。而B直接訂閱這個事件
 

這種設計有點問題
B由於要訂閱A的事件,所以B得完全引用A,其實有時候沒必要,因為我只關心A的狀態變化而已
狀態變更通知這種場景很多,有沒有更通用的方式呢?

 

解決思路

有個誰說的碰到問題加個中間層就解決了,如果解決不了就再加一層

A和B都引用ChangeToken,
B向ChangeToken註冊一個委託說:將來你有變化時回撥我這個委託
當A的狀態變化時會呼叫ChangeToken的一個方法,這個方法內部就會去觸發執行B之前塞進去的委託

此時比如有元件C、D、E..都關心A的狀態變化,也可以引用ChangeToken,並向其註冊自己的委託
這樣ChangeToken可以作為一個通用元件,在很多需要更改通知是場景中使用,如:asp.net core的配置系統、終結點路由、 ....

 

實現

微軟定義了一個IChangeToken
HasChanged:表示當前這個ChangeToken是否變化過了
ActiveChangeCallbacks:當 A觸發ChangeToken發生變化時是否主動回撥B塞進來的委託
RegisterChangeCallback(Action<object> callback, object state):提供一個方法,允許呼叫方塞入委託,B就是呼叫這個方法向ChangeToken塞入委託的。有一種情況是B希望在塞入委託的同時附帶一個狀態物件,將來委託被執行時這個狀態物件作為執行委託的引數

CancellationChangeToken是一個用的比較多的實現類,它包含一個CancellationToken屬性,這個屬性是通過建構函式來初始化的(CancellationTokenSource、CancellationToken自行查詢相關資料),
簡化的原始碼如下:

 1 public class CancellationChangeToken : IChangeToken
 2 {
 3         public CancellationChangeToken(CancellationToken cancellationToken)
 4         {
 5             Token = cancellationToken;
 6         }
 7 
 8         public bool ActiveChangeCallbacks { get; private set; } = true;
 9 
10         public bool HasChanged => Token.IsCancellationRequested;
11 
12         private CancellationToken Token { get; }
13 
14         public IDisposable RegisterChangeCallback(Action<object> callback, object state)
15         {
16               return Token.Register(callback, state);           
17         }
18     }

因為CancellationToken天然的已經現了IChangeToken,因此CancellationChangeToken只是對CancellationToken的包裝。那為啥不直接讓CancellationToken實現IChangeToken呢?我感覺是設計意圖不同,CancellationToken設計時主要是考慮應用在取消非同步操作這件事上的,只是碰巧取消非同步操作與更改通知設計思路是相似的,所以才出現CancellationChangeToken

其它的實現類沒去研究過,但是隻要你對這種設計思路理解了,碰到其它實現類應該看看就明白了

例子

下面我們使用CancellationChangeToken來完成上面的A、B類,A類狀態變化時 通知到B類(其實就是執行B類忘ChangeToken中塞入的委託),完整原始碼如下:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Console.OutputEncoding = Encoding.UTF8;
 6             CancellationTokenSource cts = new CancellationTokenSource();
 7             CancellationChangeToken cct = new CancellationChangeToken(cts.Token);
 8             var a = new A(cts);
 9             var b = new B(cct);
10             Console.ReadKey();
11         }
12     }
13 
14     public class A
15     {
16         CancellationTokenSource _cts;
17         public A(CancellationTokenSource cts)
18         {
19             this._cts = cts;
20             Task.Run(() =>
21             {
22                 Task.Delay(2000).Wait();
23                 Console.WriteLine("模擬觸發更改通知");
24                 _cts.Cancel();
25             });
26         }
27     }
28     public class B
29     {
30         public B(CancellationChangeToken cct) {
31             object testState = 1;
32             cct.RegisterChangeCallback(obj => {
33                 //將來cct檢測到變化時此委託會被執行
34                 //obj是註冊委託是傳遞進來的引數,就是這裡的testState
35                 Console.WriteLine($"狀態變化了,狀態值{obj}");
36             }, testState);
37         }
38     }

上面只是演示IChangeToken的思路,asp.net core中原始碼的應用時通常是在A中提供一個返回IChangeToken的方法

無限監控與ChangeToken.OnChange

上面的方式只能變更通知一次,下面可以永遠監控

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Console.OutputEncoding = Encoding.UTF8;
 6             var a = new A();
 7             //實現無限監控狀態變化。OnChange有兩個委託型別的引數,我們分別稱為委託1和委託2
 8             ChangeToken.OnChange(() => a.CreateChangeToken(), () => Console.WriteLine("a狀態變化了"));
 9             Console.ReadKey();
10         }
11     }
12 
13     public class A
14     {
15         CancellationTokenSource _cts;
16         public A()
17         {
18             Task.Run(() =>
19             {
20                 while (true)
21                 {
22                     Task.Delay(2000).Wait();
23                     Console.WriteLine("模擬兩秒一次觸發一次狀態修改通知");
24                     this._cts.Cancel();
25                 }
26             });
27         }
28 
29         public IChangeToken CreateChangeToken() {
30             _cts = new CancellationTokenSource();
31             return new CancellationChangeToken(_cts.Token);
32         }
33     }

重點是這句:ChangeToken.OnChange(() => a.CreateChangeToken(), () => Console.WriteLine("a狀態變化了"));
OnChange有兩個委託型別的引數,我們分別稱為委託1和委託2,當a的狀態變化後會執行委託2,之後會執行委託1,當a狀態又變化時又會執行委託2,之後執行委託1,如此往復實現無限監控 

這是今天學習的內容,可能理解得不是很準確,僅供參