標籤:rri lin 自訂 名稱 class size 維護 操作 可操作性
什麼是AOP(Aspect-Oriented Programming)?
AOP允許開發人員動態地修改靜態OO模型,構造出一個能夠不斷增長以滿足新增需求的系統,就象現實世界中的對象會在其生命週期中不斷改變自身,應用程式也可以在發展中擁有新的功能。
AOP利用一種稱為“橫切”的技術,剖解開封裝的對象內部,並將那些影響了多個類的行為封裝到一個可重用模組,並將其名為“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,
卻為業務模組所共同調用的邏輯或責任,例如交易處理、日誌管理、許可權控制等,封裝起來,便於減少系統的重複代碼,降低模組間的耦合度,並有利於未來的可操作性和可維護性。
AOP技術本質
AOP(Aspect-Oriented Programming,面向方面編程),可以說是OOP(Object-Oriented Programing,物件導向編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來建立一種對象階層,
用以類比公用行為的一個集合。當我們需要為分散的對象引入公用行為的時候,OOP則顯得無能為力。也就是說,OOP允許你定義從上到下的關係,但並不適合定義從左至右的關係。例如日誌功能。
日誌代碼往往水平地散布在所有對象層次中,而與它所散布到的對象的核心功能毫無關係。對於其他類型的代碼,如安全性、異常處理和透明的持久性也是如此。這種散布在各處的無關的代碼被稱為
橫切(cross-cutting)代碼,在OOP設計中,它導致了大量代碼的重複,而不利於各個模組的重用。
而AOP技術則恰恰相反,它利用一種稱為“橫切”的技術,剖解開封裝的對象內部,並將那些影響了多個類的公用行為封裝到一個可重用模組,並將其名為“Aspect”,即方面。所謂“方面”,簡單地說,
就是將那些與業務無關,卻為業務模組所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模組間的耦合度,並有利於未來的可操作性和可維護性。AOP代表的是一個橫向的關係,
如果說“對象”是一個空心的圓柱體,其中封裝的是對象的屬性和行為;那麼面向方面編程的方法,就彷彿一把利刃,將這些空心圓柱體剖開,以獲得其內部的訊息。而剖開的切面,也就是所謂的“方面”了。
然後它又以巧奪天功的妙手將這些剖開的切面複原,不留痕迹。
使用“橫切”技術,AOP把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,
而各處都基本相似。比如許可權認證、日誌、交易處理。Aop 的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。正如Avanade公司的進階方案構架師Adam Magee所說,
AOP的核心思想就是“將應用程式中的商業邏輯同對其提供支援的泛型服務進行分離。”
實現AOP的技術,主要分為兩大類:一是採用動態代理技術,利用截取訊息的方式,對該訊息進行裝飾,以取代原有對象行為的執行;二是採用靜態織入的方式,引入特定的文法建立“方面”,從而使得編譯器可以在編譯期間織入有
關“方面”的代碼。然而殊途同歸,實現AOP的技術特性卻是相同的,分別為:
1、join point(連接點):是程式執行中的一個精確執行點,例如類中的一個方法。它是一個抽象的概念,在實現AOP時,並不需要去定義一個join point。
2、point cut(切入點):本質上是一個捕獲連接點的結構。在AOP中,可以定義一個point cut,來捕獲相關方法的調用。
3、advice(通知):是point cut的執行代碼,是執行“方面”的具體邏輯。
4、aspect(方面):point cut和advice結合起來就是aspect,它類似於OOP中定義的一個類,但它代表的更多是對象間橫向的關係。
5、introduce(引入):為對象引入附加的方法或屬性,從而達到修改對象結構的目的。有的AOP工具又將其稱為mixin。
上述的技術特性組成了基本的AOP技術,大多數AOP工具均實現了這些技術。它們也可以是研究AOP技術的基本術語。
1.C# 手動實現實現靜態代理 <AOP在方法前後增加自訂的方法>
假設一個情境, 當使用者在系統登入, 現在需要在登入前分別做一個版本校正, 和使用者登入緩衝處理... <利用裝飾者模式實現靜態代理實現登入前後增加自訂方法>
/// <summary> /// 裝飾器模式實現靜態代理 /// AOP在方法前後增加自訂的方法 /// </summary> public class Decorator { public static void Show() { User user = new User() { Name = "Eleven", Password = "123123123123" }; IUserProcessor processor = new UserProcessor(); processor = new UserProcessorDecorator(processor); processor.RegUser(user); } public interface IUserProcessor { void RegUser(User user); } public class UserProcessor : IUserProcessor { public void RegUser(User user) { Console.WriteLine("使用者登入, Name:{0},PassWord:{1}", user.Name, user.Password); } } /// <summary> /// 裝飾器的模式去提供一個AOP功能 /// </summary> public class UserProcessorDecorator : IUserProcessor { private IUserProcessor UserProcessor { get; set; } public UserProcessorDecorator(IUserProcessor userprocessor) { UserProcessor = userprocessor; } public void RegUser(User user) { PreProceed(user); this.UserProcessor.RegUser(user); PostProceed(user); } public void PreProceed(User user) { Console.WriteLine("檢查軟體版本資訊..."); } public void PostProceed(User user) { Console.WriteLine("儲存本機快取..."); } } }
如果沒有使用AOP, 上面的需求我們要怎麼去實現, 唯一的方法就是在執行方法的前後分別新增版本資訊檢查, 和本機快取處理。
public static void Show() { User user = new User() { Name = "Eleven", Password = "123123123123" }; IUserProcessor processor = new UserProcessor(); Console.WriteLine("檢查軟體版本資訊..."); processor.RegUser(user); Console.WriteLine("儲存本地登入緩衝..."); }
2.使用Castle\DynamicProxy 實現動態代理
using Castle.DynamicProxy;//Castle.Core
/// <summary> /// 使用Castle\DynamicProxy 實現動態代理 /// </summary> public class CastleProxy { public static void Show() { User user = new User() { Name = "Eleven", Password = "123123123123" }; ProxyGenerator generator = new ProxyGenerator(); MyInterceptor interceptor = new MyInterceptor(); UserProcessor userprocessor = generator.CreateClassProxy<UserProcessor>(interceptor); userprocessor.RegUser(user); } public class MyInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { PreProceed(invocation); invocation.Proceed(); PostProceed(invocation); } public void PreProceed(IInvocation invocation) { Console.WriteLine("檢查軟體版本資訊"); } public void PostProceed(IInvocation invocation) { Console.WriteLine("儲存本地登入緩衝"); } } public interface IUserProcessor { void RegUser(User user); } public class UserProcessor : IUserProcessor { public virtual void RegUser(User user) { Console.WriteLine("使用者登入。Name:{0},PassWord:{1}", user.Name, user.Password); } } }
3. 使用.Net Remoting/RealProxy 實現動態代理
/// <summary> /// 使用.Net Remoting/RealProxy 實現動態代理 /// </summary> public class Proxy { public static void Show() { User user = new User() { Name = "Eleven", Password = "123123123123" }; UserProcessor userprocessor = TransparentProxy.Create<UserProcessor>(); userprocessor.RegUser(user); } public class MyRealProxy<T> : RealProxy { private T tTarget; public MyRealProxy(T target) : base(typeof(T)) { this.tTarget = target; } public override IMessage Invoke(IMessage msg) { PreProceede(msg); IMethodCallMessage callMessage = (IMethodCallMessage)msg; object returnValue = callMessage.MethodBase.Invoke(this.tTarget, callMessage.Args); PostProceede(msg); return new ReturnMessage(returnValue, new object[0], 0, null, callMessage); } public void PreProceede(IMessage msg) { Console.WriteLine("檢查軟體版本資訊"); } public void PostProceede(IMessage msg) { Console.WriteLine("儲存本地登入緩衝"); } } //TransparentProxy public static class TransparentProxy { public static T Create<T>() { T instance = Activator.CreateInstance<T>(); MyRealProxy<T> realProxy = new MyRealProxy<T>(instance); T transparentProxy = (T)realProxy.GetTransparentProxy(); return transparentProxy; } } public interface IUserProcessor { void RegUser(User user); } public class UserProcessor : MarshalByRefObject, IUserProcessor { public void RegUser(User user) { Console.WriteLine("使用者登入。使用者名稱稱{0} Password{1}", user.Name, user.Password); } } }
C# Aspect-Oriented Programming(AOP) 利用多種模式實現動態代理