Managed Extensibility Framework (MEF) 是用於建立可擴充套件的輕量級應用程式的庫
一、MEF是什麼
Managed Extensibility Framework (MEF) 是用於建立可擴充套件的輕量級應用程式的庫。它讓應用程式開發人員得以發現和使用擴充套件且無需配置。它還讓擴充套件開發人員得以輕鬆地封裝程式碼並避免脆弱的緊密依賴性。MEF 讓擴充套件不僅可在應用程式內重複使用,還可以跨程式重複使用。
MEF 通過組合提供了一種隱式發現它們的方法,而不是明確記錄可用元件。MEF 元件(稱為一個部件),以宣告方式詳細說明了其依賴項(稱為匯入)及其可提供的功能(稱為匯出)。當建立一個部分時,MEF 組合引擎利用從其他部分獲得的功能滿足其匯入需要。
一句話,MEF就是面向介面程式設計的應用,介面定義行為,它把例項化類放到程式碼執行的時候,通過容器引數確定。
二、MEF示例
在我們國家,不管你在哪個銀行辦理銀行卡,只要銀行卡有銀聯標識,那麼你就基本可以在任意一家銀行取錢、存錢。中國銀聯(China UnionPay)成立於2002年3月,是經國務院同意,中國人民銀行批准設立的中國銀行卡聯合組織,在境內的銀行必須都支援銀聯。也就是說,中國銀聯定義了一些行為,例如使用者可以取錢、存錢,任意一家境內銀行都必須支援銀聯定義的行為。因此,你在任意一家銀行辦理銀行卡後,就可以在所有的銀行都進行取錢、存錢等及行為。下面我們以此為例,進行一個簡單的例子:
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel.Composition; 4 using System.ComponentModel.Composition.Hosting; 5 using System.Linq; 6 using System.Reflection; 7 using System.Text; 8 using System.Threading.Tasks; 9 10 namespace MEF1 11 { 12 class Operation 13 { 14 static void Main(string[] args) 15 { 16 BlankOperation("CBC",300,100); 17 Console.WriteLine("------------------------------"); 18 BlankOperation("BOC",888,666); 19 Console.ReadKey(); 20 } 21 22 static void BlankOperation(string bankName,int saveMonenyAmout,int withdrawMoneyAmount) 23 { 24 var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); 25 CompositionContainer container = new CompositionContainer(catalog); 26 var dev = container.GetExportedValue<IUnionPay>(bankName); 27 dev.SaveMoneny(saveMonenyAmout); 28 dev.WithdrawMoney(withdrawMoneyAmount); 29 } 30 } 31 32 33 interface IUnionPay 34 { 35 /// <summary> 36 /// 存錢 37 /// </summary> 38 /// <param name="amount">存錢金額</param> 39 void SaveMoneny(int amount); 40 41 /// <summary> 42 /// 取錢 43 /// </summary> 44 /// <param name="amount">取錢金額</param> 45 void WithdrawMoney(int amount); 46 47 } 48 49 /// <summary> 50 /// 工商銀行 51 /// </summary> 52 [Export("CBC",typeof(IUnionPay))] 53 class ICBC : IUnionPay 54 { 55 public void SaveMoneny(int amount) 56 { 57 Console.WriteLine($"把錢存入工商銀行,金額為:{amount}"); 58 } 59 60 public void WithdrawMoney(int amount) 61 { 62 Console.WriteLine($"從工商銀行取錢,金額為:{amount}"); 63 } 64 } 65 66 /// <summary> 67 /// 建設銀行 68 /// </summary> 69 [Export("CCB", typeof(IUnionPay))] 70 class CCB : IUnionPay 71 { 72 public void SaveMoneny(int amount) 73 { 74 Console.WriteLine($"把錢存入建設銀行,金額為:{amount}"); 75 } 76 77 public void WithdrawMoney(int amount) 78 { 79 Console.WriteLine($"從建設銀行取錢,金額為:{amount}"); 80 } 81 } 82 83 /// <summary> 84 /// 農業銀行 85 /// </summary> 86 [Export("ABC", typeof(IUnionPay))] 87 class ABC : IUnionPay 88 { 89 public void SaveMoneny(int amount) 90 { 91 Console.WriteLine($"把錢存入農業銀行,金額為:{amount}"); 92 } 93 94 public void WithdrawMoney(int amount) 95 { 96 Console.WriteLine($"從農業銀行取錢,金額為:{amount}"); 97 } 98 } 99 100 /// <summary> 101 /// 中國銀行 102 /// </summary> 103 [Export("BOC", typeof(IUnionPay))] 104 class BOC : IUnionPay 105 { 106 public void SaveMoneny(int amount) 107 { 108 Console.WriteLine($"把錢存入中國銀行,金額為:{amount}"); 109 } 110 111 public void WithdrawMoney(int amount) 112 { 113 Console.WriteLine($"從中國銀行取錢,金額為:{amount}"); 114 } 115 } 116 }
程式碼執行後,效果如下:
三、MEF示例改進
那如果我們再增加一個銀行實現類,例如招商銀行,那麼需要在MEF1解決方案中增加一個 “CMB”類。但如果我們的程式已經運行了,此時關閉程式,就會影響使用者在四大行也無法進行取錢和存錢了,那如何在不關閉程式的前提下,還完成招商銀行接入銀聯卡呢?
答案是為每個銀行實現類建立一個解決方案,然後編譯成dll檔案,這樣我們在支援招商銀行接入銀聯時,只需要把 “CMB.dll”檔案放入對應目錄,就可以即不關閉主程式,還可以無縫支援招商銀行接入銀聯,方案目錄如下:
我們把實現銀聯的銀行的解決方案的生成目錄儲存在目錄 “..\bin\Debug\bank\”,用於測試的解決方案和銀聯介面的解決方案生成目錄儲存在目錄 “..\bin\Debug\”,編譯程式,生成檔案如下圖:
然後測試解決方案Ioc容器載入的目錄也需修改為bank目錄下:
1 using ChinaUnionPay; 2 using System; 3 using System.Collections.Generic; 4 using System.ComponentModel.Composition.Hosting; 5 using System.IO; 6 using System.Linq; 7 using System.Reflection; 8 using System.Text; 9 using System.Threading.Tasks; 10 11 namespace BankOperation 12 { 13 class Program 14 { 15 static void Main(string[] args) 16 { 17 while (true) 18 { 19 Console.Write($"請輸入銀行名稱:"); 20 string name = Console.ReadLine(); 21 BlankOperation(name, 300, 100); 22 Console.WriteLine("----------------------------------"); 23 } 24 } 25 26 static void BlankOperation(string bankName, int saveMonenyAmout, int withdrawMoneyAmount) 27 { 28 AggregateCatalog catelog = new AggregateCatalog(); 29 30 // 新增部件所在檔案目錄 31 string path = $"{Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath)}\\bank\\"; 32 catelog.Catalogs.Add(new DirectoryCatalog(path)); 33 34 // 宣告容器 35 CompositionContainer container = new CompositionContainer(catelog); 36 var dev = container.GetExportedValue<IUnionPay>(bankName); 37 38 // 動作呼叫 39 dev.SaveMoneny(saveMonenyAmout); 40 dev.WithdrawMoney(withdrawMoneyAmount); 41 } 42 } 43 }
此時執行測試程式,執行效果如下:
此時,若在程式執行的時候新增招商銀行,我們需新增招商銀行的解決方案如下,然後編譯生成:
1 using ChinaUnionPay; 2 using System; 3 using System.Collections.Generic; 4 using System.ComponentModel.Composition; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace CMB 10 { 11 /// <summary> 12 /// 招商銀行 13 /// </summary> 14 [Export("CMB",typeof(IUnionPay))] 15 public class Operation : IUnionPay 16 { 17 public void SaveMoneny(int amount) 18 { 19 Console.WriteLine($"把錢存入招商銀行,金額為:{amount}"); 20 } 21 22 public void WithdrawMoney(int amount) 23 { 24 Console.WriteLine($"從招商銀行取錢,金額為:{amount}"); 25 } 26 } 27 }
執行效果如下:
我們發現,使用MEF模式可以“高內聚,低耦合”,大大降低了程式碼的耦合,每增加一個銀行接入銀聯的時候,完全不影響其他銀行的正常業務操作。
四、示例程式碼地址
程式碼地址:https://github.com/Dwayne112401/MEFDemo.git