3.1 設計理念
3.1.4 為何決定打造 AspectW
國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
3.1.4 為何決定打造 AspectW
研究 AspectF 與其它 AOP 的實現方案比較,我們發現它使用一些近幾年才出現的程 式語言技術,使得在使用上有二個特有的優勢:
一、簡單的宣告就能開始縫合剖面功能(aspect function),因它導入了“fluent interface”[10]的設計。
二、剖面功能可簡單的擴充,因它透過“partial class”來擴展。
我們也用幾個不同角度來評估,從使用方法、從應用情境、從邏輯推理等。
從使用方法來看
以使用的方式來比較,整理起來有二類:template class 與 tag。
舉例來說:Spring(請參考圖 2.12、圖 2.13、圖 2.14)、AspectJ 等在比較早的 版本的都是繼承或引用事先做好的樣板類別,再依各案例需要自訂加入剖面功能。在較 新的版本也都可以用貼標籤的方式來使用。這種貼標籤方式在 C#的技術名稱為
Attribute;Java 的技術名稱叫 Annotation。這些標籤可以參數化。
圖 3.2 AspectJ 重試標籤簡化範例
‧ 國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
圖 3.2 是用 AspectJ 實作了一個重試標籤的簡化範例。此圖例中只秀出如何使用。
在圖例 3.2 中,標籤“@RetryOnFailure(attempts = 3,delay = 2000)”代表當例外發生 時先稍停2秒再重試,最多重試3次。這個標籤是自訂的。若要實作要完成二件主要工 作,一、宣告一個介面“@interface RetryOnFailure”,二、再實作一個 Aspect Class
“RetryOnFailureAspect”。詳細實作可參考這兩篇參考文章[14][15]。
實作過程要引入 AspectJ package,只要遵循 AspectJ 定義好的開發規則與順序還 滿容易實作出來的。若使用從 AspectF 改良過的 AspectW 同樣的邏輯程式碼大概如圖 3.3 所示。
AspectW 的語法結構延用 AspectF 的設計,並改良剖面功能(aspect function)的部 份,讓它更通用於各種狀況。AspectW 的語法可參考圖 3.10 的說明。先用
“AspectW.Define” 來表示將包裹一塊程式碼區塊,接下來串接剖面功能指令,此例是
“Retry”,第三段的 Do()函式負責將目標程式碼區塊包裹起來。
比較 AspectW 的用法,它不屬於 template class 也不是 tag,而是一串函式鏈。這 個鏈長度是動態的,函式串接起來修飾共同的目標程式碼區塊 --- 由 Do()函式負責包 裹。若用 GoF design pattern 來分析的話,最接近的應該是 Decorator pattern。Do() 函式的參數其實是個 lambda expression。
圖 3.3 AspectW 重試範例
‧
從使用面來說,AspectW 似乎比較麻煩一些些。我們再仔細看一下程式碼。圖 3-2 的標籤是貼在函式宣告位置,也就是標籤跟函式是綁在一起的在定義時期就固定了。而 傳統的縫合過程必須另外撰寫 Aspect Class 負責縫合,在主流程是看不到是否被逢合,
優點是物件化後重複可用性可以提高的,但它的優點也是缺點,整體流程的可讀性因而 降低。再比較圖 3.3,AspectW 的語法不像貼標籤與函式宣告綁在一起,而是直接在主 流程上宣示並縫合,不再另外建立 Aspect Class。直接把主功能邏輯與非主功能機制程 式碼組合起來,優點是整體流程是清楚的。也有人會認為這樣重複可用性不就無法提
〔挑選商品〕=>〔選擇配送方式〕=>〔選擇付款方式〕=>〔結帳〕
在設計流程結構階段時,導入物件化分 份,比如 logging,在每道子順序都要加上在出錯時追查或偵錯使用。比如第1道要看
圖 3.4 購物車結帳單計算程序
‧ 國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
是否登入認證仍有效,驗證輸入參數是否合法。第2道、第3道要加入 retry 以應對在 讀取庫存時網路的暫態性失效問題。
使用 AspectJ 加入這些非主功能機制的話,在主流程的程式碼是完全看不到的,因 為分散到 Aspect Class 了。只是單獨閱讀 Aspect Class 程式碼是看不出任何的程序關 聯,因為都是關節點(concern)。若改用 annotation 貼標籤也是在 class 定義端,在主 流程引用時還是看不到的。
使用 AspectJ 的好處是,主流程的程式碼是完全不須任何更動的。因為 Aspect Class 是另外再建立的。AspectJ 支援萬用字元與分群設定,在初期可少寫一些縫合設定。然 而,整體流程其實包函了主功能邏輯與非主功能機制兩部份,在上線初期有降偶合的好 處。但只要上線維護時間一久,溫水煮青蛙效應,使用者業務需求不斷調整,主功能邏 輯程式碼也會配合不斷微加微減,非主功能機制有時也要更動,而這整體流程的分離將 造成設計意圖隨著時間可讀性與理解度不斷降低。初期以萬用字元或分群的設定,容易 造成加了不該加的或少了不該少的非主功能機制,又因分散不易查覺。
圖 3.5 購物車結帳單計算程序 with AspectF
‧
採用 AspectF/AspectW 的方法就不會 有過度細化的維護一致性問題存在。使用 AspectF/AspectW 在整體流程的順序上,
可以為每道順序各別標上各道需要的剖面
‧ 國
立 政 治 大 學
‧
N a tio na
l C h engchi U ni ve rs it y
從邏輯推理來看
整理了研究心得,業務處理的整體流程除了主功能邏輯還有非主功能機制與一些鷹 架碼,其中鷹架碼是相依存在且量不大可以忽略。可以用數學式字寫下:
整體流程 = 主功能 + 非主功能 --- (公式 1)
有些非主功能機制在某些執行平台或開發平台有直接支援,只須引用平台指定的使 用規則即可。有些非主能機制可以元件化或模組化包裝成剖面功能,在傳統上一般被稱 做小工具(miscellaneous),但總是被視為可有可無,若小工具設計的好那就大受歡迎,
若設計的不好也沒人在乎的樣子,其實是有人在意的只是不明顯,這部份讓人在意的“小 雜事”通常就由當事的開發人員認份的吸收了。
AOP 的組成也可以化成三個部份:主功能、剖面功能、縫合區,用數學描述如下:
AOP ∈ {主功能,剖面功能,縫合區} --- (公式 2)
然而,在整體流程中的非主功能若是有重複使用性的,最典型的就是 log,就可以 轉化成元件或共用函式來使用,也就是剖面功能。寫成數學式子如下:
非主功能 → 剖面功能 --- (公式 3)
要注意一點,非主功能並非就是剖面功能,而是轉化成剖面功能。剖面功能通常不 能單獨存在,需要縫合時補入一些參數才能運作,就算是 log 也要參數指明要記錄那個 點。當然有些資訊可以由系統環境取得,但畢竟不是自己本身產出。所以再重寫一下數 學式子如下:
非主功能 = 縫合區 + 剖面功能 --- (公式 4) 把公式 1 與公式 4 聯結起來就變成了:
‧
話,AspectJ 是物件導向的設計,相對的 AspectF 則是程序導向,只是相對的,因為 AspectF 本身可是物件。縫合面的話,AspectJ 的確強大,這在前面〈章節 2-4 AspectJ〉有介紹。功能強大的縫合導致組織縫合的工作最終也變得相對複雜。反而 AspectF 的縫 合功能簡單,但也相對的容易使用多了。在開始使用 AspectJ 前必須先引入其函式庫。
AspectF 也是要引入函式庫,不過兩者相比 AspectF 只有一個類別。在使用 AspectJ 前 須先學習一些前置知識才能開始使用。AspectF 的學習曲線相對的低多了。在縫合部份 AspectJ 要另外建立 Aspect Class 組織主功能類別與非主功能類別的縫合關係,可以套 用萬用字元機制一次與多個目標建立縫合關係。一個主功能類別也能接受多個剖面類別 的縫合。也就是 AspectJ 的主功能類別與負責縫合的剖面類別是多對多關係。而 AspectF 的主功能區與縫合區只有簡單的一對一關係,不過使用上很有彈性可以很流利的增減剖 面功能。另一個 AspectF 最跳躍的特性是它的主功能區與縫合區是合成一體的。在擴充 的比較 AspectJ 的手續也相對的比 AspectF 多,而 AspectF 只是寫個函式即可。在長期 維護上,以經驗來分析,因使用者需求會不斷調整,微調參數加入新特徵等等,就像是 溫水煮青蛙,長期下來可以推測因為整體流程的主功能與非主功能分離的關係,維護難 度將隨時間上昇。而 AspectF 完全相反,主功能與非主功能一體,維謢難度可以持平。
主要技術面 AspectJ 使用已研究多年的 Spring,內有多種機制有 dynamic-proxy、
‧
byte-code weaving、load time weaving 等等複雜的技術。而另人訝異的是 AspectF 的主要原理是 lambda expression。以下整理成比較表格。
表 3.1 AspectJ 與 AspectF 比較表
AspectJ AspectF
主要特性 物件導向 程序導向
應用範圍 可普遍應用各領域,尤其是 plug-in。
較適合商務領域,即靠近終端
2) annotation
縫合在定義時期就綁定。
weaving
與流程縫合成一體可隨需要 流利的(fluent)增減。
擴充上比較 advice class realize annotation
aspect function
長期維護 因為分散,長期需求變動後可 讀性反而降低,並導致越來越 難維護。
因為集中,長期可讀性可維 持,維護難度持平。
主要技術 Spring dynamic-proxy byte-code weaving
lambda expression
學習曲線 相對較高 相對低很多