動態代理 — 代理模式的最高境界

  和往常同樣,小吳最後一個來到工位上,用腳點開主機的按鈕,伴隨着主機箱裏傳出的卡車啓動般的轟轟聲,一天的快樂摸魚時光又開始了......編程

  點開騰訊體育新聞,小吳正準備看看昨晚NBA的戰況如何。忽然,小吳的耳朵一陣警覺,似曾相識的腳步聲愈來愈近,,,小吳心想:難道,我上班摸魚被老闆發現了??,,關掉手機正在播放的NBA精彩十佳球,小吳若無其事地緩緩擡起頭,把目光挪向眼前的電腦屏幕,眉間一皺,左手鍵盤,右手鼠標,毫無破綻,儼然一副認真搬磚的樣子。c#

  腳步聲隨即不出所料的停在小吳的跟前,擡頭一瞧,原來是項目經理大勇,小吳明白這是來活了。設計模式

  「小吳,客戶有個新需求,XX項目客戶須要記錄每個業務操做的時間和操做人信息,你來作一下這個功能吧,客戶但願明天就能上線這個功能。」框架

  「哦。。哦。。。好的。」,小吳知道今天註定不會是快樂摸魚的一天,可是爲了儘快能摸上魚,小吳快速的打開了XX項目,開始分析需求......ide

  項目的原始業務代碼是這樣的:函數

 1     //業務類接口
 2     public interface IBLLClass
 3     {
 4         void DoThing1();
 5         void DoThing2();
 6     }
 7 
 8     //業務類
 9     public class BLLClass : IBLLClass
10     {
11         public void DoThing1()
12         {
13             //DoThing1的業務邏輯
14             Console.WriteLine("執行DoThing1...");
15         }
16 
17         public void DoThing2()
18         {
19             //DoThing2的業務邏輯
20             Console.WriteLine("執行DoThing2...");
21         }
22         
23     }

  若是是一年前的小吳看到這樣的代碼,二話不說,他會直接往業務類中硬生生地插入要增長的功能,就像這樣:學習

 1     //業務類
 2     public class BLLClass : IBLLClass
 3     {
 4         public void DoThing1()
 5         {
 6             LogUserOperation();
 7 
 8             //DoThing1的業務邏輯
 9         }
10 
11         public void DoThing2()
12         {
13             LogUserOperation();
14 
15             //DoThing2的業務邏輯
16         }
17         
18         private void LogUserOperation()
19         {
20             //記錄每個業務操做的時間和操做人信息
21         }
22     }

  可是,這時的小吳腦子裏想到的是他在某一本講設計模式的書上看到的那句話:儘可能避免編寫對原系統代碼侵入性強的代碼spa

  何謂侵入性強的代碼,以上的代碼就是一個很好的例子:它直接將須要新增的業務代碼插入到原來的業務代碼中。這就帶來了一個問題,若是你是用這種方法頻繁地去擴展你的業務代碼,那麼過不了多久,你就很難再理清你原來最基本的業務邏輯是什麼樣的了,到最後會發現本身的代碼面目全非。設計

  因此,懶惰又聰明地小吳很快就想起了前不久在書上看到的代理模式,說時遲,那時快,小吳在鍵盤上「咔咔咔」一頓騷操做,以迅雷不及掩耳之勢敲完了代碼,哼,單身22年的手速可不是蓋的,你懂得,嘿嘿嘿。。。額。。跑題了。。。我們回頭來看看小吳寫的代碼有多騷:3d

 1     //業務代理類
 2     public class BLLClassProxy : IBLLClass
 3     {
 4         private IBLLClass bllClass = new BLLClass();
 5 
 6         public void DoThing1()
 7         {
 8             //添加的業務邏輯
 9             LogUserOperation();
10 
11             //調用原始邏輯
12             bllClass.DoThing1();
13         }
14 
15         public void DoThing2()
16         {
17             LogUserOperation();
18 
19             bllClass.DoThing2();
20         }
21 
22         private void LogUserOperation()
23         {
24             //記錄每個業務操做的時間和操做人信息
25         }
26     }

   若是你認真看過上面的代碼就會明白,BLLClassProxy這個類實現了BLLClass中所具有的全部業務函數,並且最終的業務邏輯都是來自對BLLClass類中業務函數的調用,BLLClassProxy類徹底能夠代替BLLClass類,並且在BLLClassProxy中還能夠任意增長額外的業務邏輯,這很好的實現了咱們須要的效果:對原始代碼沒有侵入性,且達到擴展業務邏輯的目的。咱們把BLLClassProxy稱做爲BLLClass的代理類,顧名思義,前者代理了後者全部的業務邏輯,故稱之爲代理類

  那麼咱們將會在實際的調用代碼中作以下改動:

1     IBLLClass bll = new BLLClass();      //==> 修改前:使用原始業務類
2                                  
3     IBLLClass bll = new BLLClassProxy(); //==> 修改後:使用代理業務類

  咱們看到,因爲原始業務類(BLLClass)與代理業務類(BLLClassProxy)都實現了接口IBLLClass,因此只須要將業務類聲明變量切換成代理類的實現就完成了整個需求的變更,無需作其餘改動。固然,這還要得益於業務類的建立者的先見之明,提供了一個業務類的接口(IBLLClass),這也同時給咱們提了個醒,儘可能以接口的方式聲明業務類變量,就是所謂的「面向接口編程」。

  以上就是使用了代理模式來解決業務擴展的需求,其實再按細分的話,咱們能夠將以上的代理叫作 靜態代理。靜態代理指的就是在代碼編譯階段就已經生成代理類,而你須要在程序運行前就將代理類的代碼一五一十地寫好,若是有五十個代理類就寫五十個,有一百個就寫一百個。有人會問,啥?難不成還能有動態代理?固然,繼續看劇情發展......

懶人都愛用的「動態代理」

  項目經理大勇又一次笑眯眯地走向小吳,小吳不由心頭一緊,心想,不妙。

  「小吳啊,客戶又提了幾個需求,不過不用慌,和上次要加的那個需求同樣,只不過此次把另外那20個業務類也加上一樣的業務邏輯。」

  小吳心中即使一萬個草泥馬,可是卻依然面帶微笑,點頭「好好好」。

  大勇也不忘誇小吳兩句:「上回加的那個代理類很不錯嘛,就照着那種方法加,很快的」。

  「很快?要不你來試試?」,小吳一邊嘴裏嘀咕着,一邊在腦子裏又蹦出了個新想法。

  又要手擼20多個代理類?有沒有什麼別的方法能減小個人工做量呢,好比。。。自動生成代理類

  年輕人就怕沒想法,有想法就要付諸於行動,因而小吳開始尋思着如何實現本身想法。

  自動生成代理類?從解決問題的思路出發有兩種解決方法:

  一、寫一個代碼生成器,生成代理類的代碼,而後再進行編譯。這種方法本質上仍是咱們上面介紹的靜態代理類,只不過不用咱們手動編寫代理類的代碼,可是使用這種方法是極其痛苦的,雖然你不用手動編寫真正的代理類代碼,可是你必須去作一些代碼生成的配置吧,這個過程是及其繁瑣的,一個字歸納,「太他媽LOW了!」

  二、在程序編譯好後跑起來的過程當中,動態生成代理類。說的是什麼?程序都已經跑起來了還能生成代理類?不錯,這就是動態生成代理類,藉助的是c#語言的Emit高級特性。

  顯然咱們須要的是動態生成代理類,那麼這麼說的話咱們還必需要再學一學Emit建立類?小吳以爲照這樣下去這事兒得黃了,這已經不是臨陣磨槍了,這是要臨陣造輪子啊,這活還幹不幹了,等着造完這個輪子,黃花菜都涼了。

  因而當天晚上祖師爺就託夢給小吳,讓小吳用一用 Castle 框架,如獲至寶的小吳次日就按照祖師爺的指示用上了Castle框架,嗯,真香~~

  咱們在使用技術中大部分都是用既有的輪子,造輪子這種事情讓小吳幹,小吳可不樂意。固然,造輪子對於咱們的學習仍是頗有用處的,嘗試着去造造輪子,會提高你對技術更深層次的理解,由於你再也不只是站在使用者的角度簡單地看問題。

  咱們來看看Castle是怎麼使用的:

  一、首先引用 Castle.Core 的dll文件

  二、建立建攔截器,攔截器的意思很是直觀,就是把運行着的方法攔下來,等會兒,你先別運行,先執行完我給你的方法A,再運行你本身的方法,而後再運行我給你的方法B......也就是說經過攔截器你能夠在原始業務邏輯的先後或者任意可切入的點插入你想加入的業務邏輯。Castle 框架提供了一個標準的攔截器類 StandardInterceptor:

1     public class StandardInterceptor : MarshalByRefObject, IInterceptor
2     {
3         public StandardInterceptor();
4 
5         public void Intercept(IInvocation invocation);
6         protected virtual void PerformProceed(IInvocation invocation);
7         protected virtual void PostProceed(IInvocation invocation);
8         protected virtual void PreProceed(IInvocation invocation);
9     }

  你能夠重寫 PreProceed、PerformProceed、PostProceed 三個虛方法,它們表示三個攔截點:執行前、執行中、執行後,你能夠加入本身的業務邏輯到這三個攔截點裏,實現業務的添加。

  因而像這樣創建一個攔截器類:

 1     public class LogInterceptor: StandardInterceptor
 2     {
 3         protected override void PostProceed(IInvocation invocation)
 4         {
 5             LogUserOperation();
 6         }
 7 
 8         private void LogUserOperation()
 9         {
10             //記錄每個業務操做的時間和操做人信息
11             Console.WriteLine("記錄日誌");
12         }
13     }

  而後這樣編寫建立業務類變量的代碼:

1     //代理生成類
2     ProxyGenerator proxyCreator = new ProxyGenerator();
3 
4     //經過Castle動態生成代理類
5     IBLLClass bll = proxyCreator.CreateInterfaceProxyWithTargetInterface<IBLLClass>(new BLLClass(), new LogInterceptor());

  咱們能夠看到最終業務類實例的建立是藉助於Castle框架來生成的,其實Castle框架就替咱們完成了動態代理類的生成,咱們只須要在攔截器中寫好咱們須要加入的攔截方法體代碼,而後注入到代理類。固然這裏只是提供了使用Castle框架一個很簡單的例子,其實Castle中還有不少豐富的功能,有待各位看官根據本身的需求去發掘,師傅領進門,修行靠我的嘛,就是這麼個道理。最後展現一下運行結果:

  

 

  動態代理能夠稱的上是代理模式的最高境界,由於它藉助動態生成的技術讓程序在運行過程當中自動根據註冊條件生成代理類,省去了手寫靜態代理類的麻煩。同時動態代理模式也是AOP(面向切面編程)的一種很好的實現方式。

相關文章
相關標籤/搜索