當我會AOP之後,去丈母孃家都是挺著胸脯!
當和朋友談到AOP時,第一映像會說AOP的應用層面,比如攔截器、過濾器,實現複用等等使用層面上。
這些應用層面上的回答遠遠沒有深入AOP的思想。是的,AOP是思想,面向切面思想。
既然是程式設計思想,那我們就有必要了解AOP的整個過程,以及AOP思想出現的原因。
AOP面向切面程式設計思想,能夠解決什麼問題,我們回顧一下程式設計思想的發展路線......
早期的POP面向過程程式設計,即是以功能為中心來進行思考和組織的一種程式設計方法,強調的是功能。
分析解決問題所需要的步驟,然後用函式把這些步驟一一實現,使用的時按順序依次呼叫,嚴格按照順序,側重解決步驟,著眼區域性或具體。
實際是一種單一的思考方式,符合人類的思考方式,是一種基礎的方法,從實際出發。
它能夠流程化程式設計任務,只需要考慮實現方式和最終結果;開發效率高,程式碼短小精悍,善於結合資料結構來開發高效率的程式;明確流程,步驟清楚,便於節點分析。
但是,需要深入思考,比較耗費精力;程式碼複用性低,不易擴充套件,維護難度大,且面向過程的模組化難度較高,耦合度也高。
由此可見,隨著時代發展,面對的系統逐漸複雜,一般的POP無法滿足,於是出現了OOP面向物件程式設計思想。
需要說明的是,並不是因為OOP的出現,才使得POP沒有餘地,或許有不少的夥伴甚至沒聽過POP。POP和OOP其實是一種互補關係,相關複雜問題拆解之後還是會迴歸到面向過程的思想。
OOP,面向物件程式設計,以物件為中心,複雜系統出現時,盛行的一種新型的程式設計思想。
通常脫口而出“萬物皆物件”,它可以搭建大型的複雜的系統,它是將資料抽象為模組結構,然後必須存在某種方式來實現程式碼的多型執行,最後它能壓縮部分程式碼和函式。
或許不是很好理解。
舉個例子,比如說:我們的系統是一個建築,那類/物件就是磚塊,磚塊能夠組成牆,牆能構成房間,房間組合在一起就組成了一棟建築。
這是不是和我們面向物件程式設計時是一樣的?一個模組由多個類共同實現,模組又組成了某項服務,多個服務構成了一個完整的系統。
那麼,POP能夠滿足精簡系統的開發,OOP能夠滿足複雜的系統,為什麼還會出現AOP呢?
首先,構建了一個系統之後,依然會有需求的存在,有了需求就會難免調整程式碼,那麼類就會發生變化,如果大規模變化將是不現實的,整個系統就會存在隱患以及人力物力的多餘需求。
類應該是固定的,不應該頻繁更改,之所以出現了那麼多的設計原則和設計模式,目的就是針對不斷的需求變化而進行拆解分離,使我們的類能夠像磚塊一樣固定,從而讓系統穩健。
我們設計完一個系統之後,新增需求要增加一個日誌列印模組,授權模組,如果在各個模組都新增的話改動的工作量無疑是很大的,存在的隱患也是很大的。
從物件的組織角度來講,類是繼承關係,適合縱向擴充套件,這也是OOP的思想。
就像我們的建築,打好地基穩固之後,縱向增加樓層是相對較方便的,但是橫向擴充套件是很困難的。
因此,面向物件設計有兩個缺陷:
1.共性問題,面向物件設計一般是縱向思維,總會有一些共性不足。
2.擴充套件問題,當我們需要對現有的物件動態的新增某些行為或責任時就會變得比較困難。
於是,AOP出現了,來彌補OOP的共性問題和擴充套件問題。
當然,AOP不是OOP的升級版,是對OOP缺陷的補充。
POP,OOP,AOP 這三種程式設計方式,本質也是互相彌補,從來不是哪個盛行而其他的就需要了。
那麼到底什麼是AOP呢?
AOP面向切面程式設計,切面就是橫切關注點模組化,OOP是使用類狀態(屬性)和行為模組化。
類和介面組織的,是針對橫向關注點,跨越應用程式的多個模組的功能需求。
AOP涉及的應用,例如:日誌記錄、效能統計、安全控制,異常處理。都是可以從業務程式碼中劃分出來,非業務邏輯的方法。
AOP善於將公共的功能提取出來,成立公共的模組,只關注通用的功能,不關注業務邏輯。
AOP的優勢:
1.將通用的功能從業務邏輯中抽離出來,提高程式碼的複用性,有利於後期的維護和擴充套件。
2.在軟體設計時,抽出通用功能,有利於軟體設計的模組化,降低架構的複雜度。
AOP與 OOP所針對的目標是不同的。OOP針對業務處理過程的實體、屬性,行為進行抽象封裝。
AOP則是針對業務處理過程中切面進行提取,就是面向物件過程中的某個步驟或階段的提取,來降低邏輯過程中各個部分的耦合。
因此,相比於OOP,可以總結出AOP的特點。
1.面向目標不同,OOP面向名詞,AOP面向動詞。
2.思想結構不同,OOP是縱向,AOP是橫向。
3.注重方面不同,OOP偏重業務邏輯單元劃分,AOP偏重業務處理過程中的某個步驟。
那麼,我們在AOP應用層面的實現,有兩種常見的方式。
-
靜態代理AOP,就是手寫程式碼。
我們以裝飾器模式為例子,裝飾器就是在一個原有物件的情況下封裝一個新的裝飾器類,來包裝原有類,提供額外的功能。
//介面服務
public interface ITravelService
{
Task PlanToTravel(Travel travel);
}
public class TravelService : ITravelService
{
public Task PlanToTravel(Travel travel)
{
Console.WriteLine($"旅客{travel.name}成功加入旅行團");
return Task.CompletedTask;
}
}
//裝飾器
public class TravelDecorator : ITravelService
{
private readonly ITravelService _travelService;
public TravelDecorator(ITravelService travelService)
{
_travelService = travelService;
}
public async Task PlanToTravel(Travel travel)
{
//旅行前
await IsAllowToTravel(travel);
await _travelService.PlanToTravel(travel);
//旅行後
await IsAllowBack(travel);
}
public Task IsAllowToTravel(Travel travel)
{
Console.WriteLine($"目前處於疫情嚴重,將停止旅遊!");
return Task.CompletedTask;
}
public Task IsAllowBack(Travel travel)
{
Console.WriteLine($"返回目標地處於風控期,暫時無法返航!");
return Task.CompletedTask;
}
2.動態代理實現,可以通過反射來實現。
例如現有的AOP框架,Romoting(分散式通訊框架)、Castle輕量級容器(包含實現ORM、IOC、MVC,AOP)、Unity(IOC容器,AOP)。
以 Remoting和Castle 分別寫一個例項,如下:
//Remoting 例子
public class DynamicProxy<T> : DispatchProxy where T : class
{
public T Target { get; set; }
public DynamicProxy(){}
//執行之前執行邏輯
public Action BeforAction { get; set; }
//執行之後執行邏輯
public Action AfterAction { get; set; }
public static T Decorate(T target, Action BeforAction, Action AfterAction)
{
// DispatchProxy.Create creates proxy objects
var proxy = Create<T, DynamicProxy<T>>() as DynamicProxy<T>;
proxy.AfterAction = AfterAction;
proxy.BeforAction = BeforAction;
// If the proxy wraps an underlying object, it must be supplied after creating the proxy.
proxy.Target = target ?? Activator.CreateInstance<T>();
return proxy as T;
}
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
//執行業務之前的程式碼邏輯
BeforAction();
var result = targetMethod.Invoke(Target, args);
//執行業務之後的程式碼邏輯
AfterAction();
return result;
}
}
//remoting 例項 使用方式
var tralvel = new Travel
{
name = "張三",
date = DateTime.Now,
telphone = "110",
travel_target = "HangZhou"
};
var service = new TravelService();
var proxyService = DynamicProxy<ITravelService>.Decorate(service,
() => { Console.WriteLine("執行邏輯前"); },
() => { Console.WriteLine("執行邏輯後"); });
proxyService.PlanToTravel(tralvel);
//castle 例項
public class CastleProxy<T> : IInterceptor where T : class
{
private static readonly ProxyGenerator _generator = new ProxyGenerator();
public T Target { get; set; }
public CastleProxy(){}
//執行之前
public Action BeforAction { get; set; }
//執行之後
public Action AfterAction { get; set; }
public static T Decorate(T target, Action BeforActions, Action AfterActions)
{
var proxy = target != null ?
_generator.CreateInterfaceProxyWithTarget(target, new CastleProxy<T>()
{
BeforAction = BeforActions,
AfterAction = AfterActions
}):
_generator.CreateInterfaceProxyWithTarget<T>(Activator.CreateInstance(typeof(T)) as T,new CastleProxy<T>()
{
BeforAction = BeforActions,
AfterAction = AfterActions
});
return proxy;
}
public void Intercept(IInvocation invocation)
{
try
{
BeforAction();
invocation.Proceed();//程式執行
AfterAction();
}
catch (TargetInvocationException error)
{
throw error.InnerException;
}
}
}
//castle 例項 使用方式
var tralvel = new Travel
{
name = "張三",
date = DateTime.Now,
telphone = "110",
travel_target = "HangZhou"
};
var service = new TravelService();
var proxyService = CastleProxy<ITravelService>.Decorate(service,
() => { Console.WriteLine("執行邏輯前"); },
() => { Console.WriteLine("執行邏輯後"); });
proxyService.PlanToTravel(tralvel);
好了,以上就是AOP面向切面程式設計思想的內容了,希望我講解的內容能夠幫到大家。哈,希望夥伴們給個小心心!