代理模式是什麼?如何在 C# 中實現代理模式

代理模式是什麼?如何在 C# 中實現代理模式

代理模式 並不是日常開發工作中常常用到的一種設計模式,也是一種不易被理解的一種設計模式。但是它會廣泛的應用在系統框架、業務框架中。

定義
它的 定義 就如其它同大部分 設計模式 的定義類似,即不通俗也不易懂,而且隨便百度一下就能找到 : 爲其他對象提供一種代理,以控制對這個對象的訪問。代理對象在客戶端和目標對象之間起到中介的作用。

每個字都認識,連在一起就看不懂了 by. 某個攻城獅

我們一個詞一個詞看就明白了。

其他對象
所謂的 其它,其實就是你係統中 任意 一個類型,可以是 UserService、OrderRepository、DataDeletedEventListener、等等。

控制對這個對象的訪問
訪問 其實就是調用這個對象上的方法、訪問它的屬性、設置它的屬性等等,比如

User user = UserService.GetUserById(1); // 訪問了 GetUserById 方法
int id = user.Id; // 訪問了 Id 屬性
Order order = OrderRepository.SelectByUserId(id); // 訪問了 SelectByUserId 方法
控制訪問 ,控制 的本質是包裝,外部不再直接使用 其他對象 ,而是使用 代理 ,再由代理來訪問 其它對象。我們可以使用一個已有的 List 實現一個 IList ,並在使用 Add 方法時,打印一行日誌。

public class LogList : IList
{
// other code here..

private IList raw; // 這個就 "其它對象"

public EventList(IList raw)
{

  this.raw = raw; // 通過構造函數,這可以讓 EventList 控制對 IList<T> 的訪問。

}

public void Add(T value)
{

  this.raw.Add(value);
  Console.WriteLine(value);

}
}
上面就是一個簡單的代理模式的例子:
爲 IList 提供一種 LogList ,以控制對 IList 的訪問。

實現
簡單實現
上面 LogList 就是一種簡單的實現。

但是你無法對這個類做外部擴展,所有的邏輯都在類型的內部被固定了。

於是我們可以使用下面的方法創建一個可以靈活擴展的 ListProxy

public interface IListInterruption
{

// other codes

// 執行 IList.Add 時會進入的方法
void OnAdding(IList<T> list, T addingValue);

// 執行完 IList.Add 時會進入的方法
void OnAdded(IList<T> list, T addedValue);

// other codes

}

// 列表代理類
// 允許外部提供 IListInterruption 來豐富 ListProxy 的邏輯。
public class ListProxy : IList
{

private readonly IList<T> raw;
private readonly List<IListInterruption> interruptions;

public ListProxy(IList<T> raw)
{
    this.interruptions = new List<IListInterruption>();
    this.raw = raw;
}

public void AddInterruption(IListInterruption interruption)
{
    this.interruptions.Add(interruption);
}

public void Add(T value)
{
    foreach(var item in this.interruptions)
        item.OnAdding(this.raw, value);

    this.raw.Add(value);
    
    foreach(var item in this.interruptions)
        item.OnAdded(this.raw, value);
}

}
上面的代碼實現一個較爲靈活的 ProxyList 。

首先看看 IListInterruption。通過實現 IListInterruption 接口,可以向 ProxyList 提供各種各樣的功能。

我們可以看一個簡單的功能

public class LogListInterruption : IListInterruption
{

// other codes

public void OnAdding(IList<T> list, T addingValue)
{
    Console.WriteLie("Adding : {0}", addingValue);
}

// other codes

}
向 ProxyList 添加上述組件,就可以實現在 Add 前打印待添加的值的功能。

List myList = new List();
ProxyList proxy = new ProxyList(myList);
proxy.AddInterruption(new LogListInterruption());
proxy.Add(1);
// >> Adding : 1
這種實現方式可以創建出針對某個類型的代理類,並通過外部給予的 IListInterruption 來豐富代理類功能。

但缺點是,當你無法爲所有的類型都創建 Proxy 和 Interruption 。

動態代理類
之前的方法中,我們在編寫階段就已經建立了代理類,被稱爲靜態代理類。

這種方法無法將代理類運用在系統中任何一個我們可能需要的類型上。

於是,動態代理類 就來了。

動態代理類 依靠編程語言本身的特徵,讓程序在 運行時 創建類型,實現接口,添加功能等等。

在 C# 中可以通過兩種方式實現運行時創建類型的功能

CodeDom + 運行時編譯
Emit
CodeDom 可以用來生成 C# 代碼,再利運行時編譯,會將C#代碼編譯爲內存中的程序集,最後通過反射訪問程序集中的成員。
這種方法的特點就是慢。。。。因爲要生成語句,還要編譯,生成程序集,最後還要反射,都是大開銷,所以慢是可想而知的。

Emit 提供了利用 IL 命令在運行時創建類型、方法,並填充方法內的功能。
畢竟 C# 最終就是編譯成 IL 的,所以直接使用 IL 性能當然快無敵了。

這個方式的缺點只有一個 : 學習 IL 。這玩意可不是每個人都願意去學的。。。

於是,選擇一些已經利用 Emit 做好了動態代理類功能的第三方功能庫,成爲了一個很好的選擇。

C# 大環境下,可以用來生成動態代理類的庫一般有兩個選擇 :

PostSharp
Caslte.DynamicProxy
其中 PostSharp 使用了更復雜的技術,不是使用 Emit,而且在編譯時,就將代理類所附加的功能放進了目標類型中,你可以通過 Attribute 向任意的方法添加額外的功能。

PostSharp 會在程序編譯時,把這些 Attribute 的功能直接編譯到方法內部。
這種在編譯時就把一切準備好的方法,讓 PostSharp 有着最高的運行性能。
但是又傻瓜、又好用的 PostSharp 只有一個缺點 ———— 收費。

Castle.DynamicProxy 是免費的,他是利用 Emit 在程序運行時創建代理類的。
使用 Castle.DynamicProxy 有兩個步驟:

編寫 Interceptor
將 Interceptor 附加到某個類型或接口上,並得到一個會調用你的 Interceptor 的代理類實例
開發流程很像之前的 LogList 的例子。

相比較 PostSharp 那種一個 Attribute 就搞定一切的模式, Caslte.DynamicProxy 就沒有那麼方便了。

那麼一個顯而易見的問題就來了 :
能不能利用 Caslte.DynamicProxy 實現像 PostSharp 那樣利用 Attribute 創建代理類的功能呢?

Reface.AppStarter.Proxy
這是基於 Reface.AppStarter 開發的一個功能模塊,
使用它,可以利用 Attribute 的方式輕鬆的創建代理類,並實現 AOP 的功能。

你所要做的,就是創建一個繼承於 ProxyAttribute 的特徵。

ProxyAttribute 中有三個方法需要重寫

OnExecuting ,被標記的方法執行時
OnExecuted ,被標記的方法執行後
OnExecuteError , 被標記的方法執行出現異常後
你可以編寫你的邏輯在這三個方法內,並將你的 Attribute 掛載到你需要的類型的方法上即可。

剩下的事情只有兩件

向你的 AppModule 添加 ProxyAppModule
爲你需要創建代理的類型加上 [Component] 特徵
你已經完成了所有工作,
當你利用 Reface.AppStarter 的框架的 IOC / DI 容器創建你的類型時,實際得到的就是代理類,這些代理類會調試你給予的 ProxyAttribute 。

關於 Reface.AppStarter.Proxy 的細節,會在以後的文章中進一步介紹。

原文地址https://www.cnblogs.com/ShimizuShiori/p/12752893.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章