[.NET][Architecture][Design Patterns] 切面導向設計 (Aspect-Oriented Programming, AOP) 的平台實作 (1) - 概念

切面導向設計是一個很有趣的技術與設計架構,它可以允許開發人員在程式執行時期在方法 (method) 中植入共用的一些操作,而且不需要由開發人員自己加,直接在核心系統中註冊就能得到植入操作的功能,最常見的例子就是記錄 (logging)...

切面導向設計是一個很有趣的技術與設計架構,它可以允許開發人員在程式執行時期在方法 (method) 中植入共用的一些操作,而且不需要由開發人員自己加,直接在核心系統中註冊就能得到植入操作的功能,最常見的例子就是記錄 (logging),一般來說如果要寫記錄功能的話,通常我們會這樣寫:

 

public int Calculate(int X, int Y)
{
    Log.WriteMethod("Calculate");
    Log.WriteParams(X, Y);
    return X + Y;
}

 

當到了AOP時代,我們只要這樣寫:

 

[Logging(MethodInfo.Name, MethodInfo.Parameters)]
public int Calculate(int X, int Y)
{
    return X+Y;
}

 

和上面的相比,可以發現程式的編寫簡單許多,只要在要記錄的方法上加上特徵項 (attribute),就能達到和在方法內呼叫記錄的函式有同等的功能,對於程式開發者來說能省下許多時間,但對於 Framework 開發者而言則是一個大挑戰。

AOP的概念其實很簡單,只是要在程式執行的期間植入一些大多數功能都會用到的一些服務,諸如安全檢查或是記錄動功能,以降低或省去開發人員編寫這些程式的負擔,但因為 AOP 是會影響到物件內所有方法呼叫的一種架構,當物件的方法被呼叫時,就必須要在實際執行該方法前 (或後) 來執行要植入的功能,在一般的程式設計來說是一件幾乎不可能的任務,因為程式編寫後,基本上是不能變動的,除非對系統程式很熟悉,利用一些特別的技法將呼叫擱置並先呼叫要植入的方法,再執行攔置的呼叫,這部份是難度較高的地方。

 

Horizontal and Vertical Concerns

 

在 GoF 所提出的 23 項 Design Patterns 中,有一項稱為 Proxy Pattern,顧名思義,它是具備代理目標物件執行服務的一種設計模式,用戶端雖然是呼叫目標物件,但它的呼叫實際上是被代理物件 (Proxy Object) 攔截並處理,而執行結果仍然與直接呼叫目標物件相同,不過因為經過了 Proxy Object,所以我們可以在 Proxy Object 中加料來處理這些工作,也就是說我們可以在 Proxy Object 中放進要植入的服務,並且在叫用目標物件時一一的套用,就能達到我們的需求。

 

File:Proxy pattern diagram.svg

 

public interface ICalculate
{
    int Calculate(int X, int Y);
}

// proxy object
public class CalculateProxy : ICalculate
{
    public int Calculate(int X, int Y)
    {
        Log.WriteMethod("Calculate"); 
        Log.WriteParams(X, Y);
        var concreteService = new Calculate();
        return concreteService.Calculate(X, Y);
    }
}

// concrete class
public class Calculate : ICalculate
{
    public int Calculate(int X, int Y)
    {
        return X + Y;
    }
}

// client call.
var calculateObject = new CalculateProxy();
calculateObject.Calculate(1, 2);

雖然看起來是滿足了我們的需求,但是卻有個很嚴重的問題:它是靜態的實作 (static implementation),也就是它必須要透過寫程式或是使用像 CodeDOM 的模型來產生,而無法在執行時期產生,這樣等於 AOP 無法動態使用,讓導入 AOP 的困難度增加,因此就有先進引進了 Dynamic Proxy 的概念,利用 JVM/CLR 這些語言的特性,讓 Proxy Object 在執行期產生,以替代以靜態方式實作的 Proxy Object,如此一來開發人員就不需要為了實作 AOP 而編寫一大堆 Proxy Object,尤其是在系統中有大量類別時。

image

(Source: http://userpages.umbc.edu/~tarr/dp/lectures/DynProxies-2pp.pdf)

 

有了 Dyanmic Proxy 後,我們的程式就可以簡化成這樣:

 

public interface ICalculate
{
    int Calculate(int X, int Y);
}

public interface IAspect
{
    void Intercept(params object[] Param);
}

// concrete class
public class Calculate : ICalculate
{
    public int Calculate(int X, int Y)
    {
        return X + Y;
    }
}

// log
public class LogAspect : IAspect
{
    public void Intercept(params object[] Param)
    {
        // implement log service.
    }
}

// client call.
var calculateObject = ProxyFactory.GetProxyService(
       typeof(ICalculate), typeof(LogAspect), new Calculate());
calculateObject.Calculate(1, 2);

 

ProxyFactory 就是用來生成 Dynamic Proxy Object 的物件工廠,GetProxyService() 會回傳動態生成的 Proxy Object,因為都遵循相同的介面,因此用戶端只要依介面定義呼叫,而不需要為了 Proxy Object 有所改變,程式開發人員還是能依他們的認知和習慣來編寫一般的程式,但它們的程式都已經隱含了切面的功能。

ProxyFactory 可以用很多支援 AOP 的 Framework 替代,像知名的 Castle.DynamicProxy, Unity AOP, Spring AOP, PostSharp 等都是,它們都是十分成熟的 AOP Framework,若只是想應用 AOP 做專案或功能的話,這些 Framework 都很好用,但是本系列文基本上是告訴大家要怎麼刻一個 AOP Framework,而不是教大家怎麼用那些 AOP Framework (如同前面的 ORM 系列文)。

應用的文章,91 有一篇寫得很精采:http://www.dotblogs.com.tw/hatelove/archive/2014/05/04/implementation-aop-by-castle.windsor.aspx

如果等不及的話,可以先參考忠成老師的文章:http://www.dotblogs.com.tw/code6421/archive/2012/12/03/85339.aspx