[轉帖]瞭解AOP

面向對象技術很好地解決了軟件系統中角色劃分的問題。藉助於面向對象的分析、設計和實現技術,開發者可以將問題領域的“名詞”轉換成軟件系統中的對象,從而很自
然地完成從問題到軟件的轉換.
但是,問題領域的某些需求卻偏偏不是用這樣的“名詞”來描述的.我的一個朋友就曾經遇到這樣的問題:需要對系統中的某些方法進行日誌記錄,這種需要記錄方法
散佈在40多個類中。面對這種需求,應該怎麼辦呢?最直接的辦法就是:創建一個起類(或接口),將日誌的功能放在其中,並讓所有需要日誌功能的類繼承這個起類(或接口).如果這個需求是後期提出的.需要修改的地方就會分散在40多個文件(如果是C+十,這個數量還可能加倍)中。這樣大的修改量,無疑會增加出錯的機率,並且加大系統維護的難度。
人們認識到,傳統的程序經常表現出一些不能自然地適合單個程序模塊或者幾個緊密相關的程序模塊的行爲 例如日誌記錄、對上下文敏感的錯誤處理、性能優化以及設計模
式等等、我們將這種行爲稱爲“橫切關注點(crosscuttingconcern)”,因爲它跨越了給定編程模型中的典型職責界限。如果使用過用於核切關注點的代碼,您就會知道缺乏模塊性所帶來的問日。因爲橫切行爲的實現是分散的,開發人員發現這種行爲難以作邏輯思維、實現和更改.
因此,面向方面的編程(Aspect-OrientedProgramming,AOP)應運而生。AOP爲開發者提供了一種描述橫切關注點的機制,並能夠自動將橫切關注點織入到面向對象的軟件系統中,從而實現了橫切關注點的模塊化.通過劃分Aspect代碼,橫切關注點變得容易處理。開發者可以在編譯時更改、插入或除去系統的Aspect,甚至重用系統的Aspect.
更重要的是,AOP可能對軟件開發的過程造成根本性的影響。我們可以想象這樣一種情況:OOP只用於表示對象之間的泛化一特化(generalization-specialization)關係(通過繼承來表現),而對象之間的校向關聯則完全用AOP來表現。這樣,很多給對象之間橫向關聯增加靈活性的設計模式(例如Decorator、Role Object等)將不再必要.
一種編程思想是否真正優秀,只有從實現語言上才能看到。施樂公司帕洛阿爾託研究中心(Xerox PARC)開發了第一個AOP的開發環境——AsPectJ ,這個工具提供了一整套的語法,能夠清楚地描述橫切關注點,並將其織入到Java源代碼中。織入後的代碼仍是標準Java代碼,因此AspectJ不會影響Java的移植能力。此外,AspectJ提供了一個獨立的IDE,並且能夠嵌入到Jbuilder、Forte等Java開發環境之中,無縫地提供AOP的能力。關於AspectJ,讀者可以在http://www.aspectj.org找到更多的信息。
但是,現在的AOP還處於相當不完善的階段:它只能應用於很少的幾種語言環境下,並且必須掌握源代碼才能進行織入.但以RUP之父Ivar Jacobson爲代表的科學家們仍對AOP推崇備至:他認爲AOP將最終改變整個軟件開發的方式,並且更完美地實現“用例驅動”的開發思想.

 

利用AOP分離軟件關注點

一個關注點(concern)就是一個特定的目的,一塊我們感興趣的區域。從技術的角度來說,一個典型的軟件系統包含一些核心的關注點和系統級的關注點。舉個例子來說,一個信用卡處理系統的核心關注點是借貸/存入處理,而系統級的關注點則是日誌、事務完整性、授權、安全及性能問題等,許多關注點——我們叫它橫切關注點(crosscutting concerns)——會在多個模塊中出現,使用現有的編程方法,橫切關注點會橫越多個模塊,結果是使系統難以設計、理解、實現和演進。
AOP能夠比上述方法更好地分離系統關注點,從而提供模塊化的橫切關注點。
在這篇文章裏,我首先會解釋橫切關注點在軟件系統中引起的問題,接着我會介紹AOP是怎樣解決橫切關注點問題的。
軟件編程方法的演進
在計算機科學的早期階段,開發人員使用簡單的機器級代碼來編程。不幸的是,程序員得花費更多時間來考慮一種特定機器的指令集而不是手中需要解決的問題本身。慢慢地,我們轉而使用允許對底層機器做某種抽象的高級語言。然後是結構化語言,我們可以把問題分解成一些必要的過程來完成任務。但是,隨着複雜程度的增加,我們又需要更適合的技術。面向對象的編程方式(OOP)使我們可以把系統看作是一批相互合作的對象。類允許我們把實現細節隱藏在接口下。多態性爲相關概念提供公共的行爲和接口,並允許特定的組件在無需訪問基礎實現的前提下改變特定行爲。
編程方法和語言決定了我們和計算機交流的方式。每一種新的方法學都提出一種新的分解問題的方法:機器碼、僞代碼、過程和類等。每種新的方法學都使得從系統需求到編程概念的映射更加自然。編程方法學的發展讓我們可以建立更加複雜的系統,這名話反過來說也地,我們能夠建立更加複雜的系統是加爲這些技術允許我們處理這種複雜度。
現在,大多數軟件項目都選擇OOP的編程方式。確實,OOP已經表明了它處理一般行爲的能力,但是,我們將會看到(或許你已經感覺到了):OOP不能很好地處理橫越多個——經常是不相關的——模塊的行爲。相比之下,AOP填補了這個空白,它很可能會是編程方法學發展的一個里程碑。
把系統看作一批關注點
我們可以把一個複雜的系統看作是由多個關注點來組合實現的。一個典型的系統可能會包括幾個方面的關注點,如業務邏輯、性能,數據存儲、日誌和調度信息、授權、安全、線程、錯誤檢查等,還有開發過程中的關注點,如易懂、易維護、易追查、易擴展等,圖1演示了由不同模塊實現的一批關注點組成一個系統。

 


圖2把需求比作一束穿過三棱鏡的光。我們讓需求之光通過鑑別關注點的三棱鏡,就會區別出每個關注點。

 


開發人員建立一個系統以滿足多個需求,我們可以大致地把這些需求分類爲核心模塊級需求和系統組需求。很多系統級需求一般來說是相互獨立的,但它們一般都會橫切許多核心模塊。舉個例子來說,一個典型的企業應用包含許多橫切關注點,如驗證、日誌、資源地、系統管理、性能及存儲管理等,每一個關注點都牽涉到幾個子系統,如存儲管理關注點會影響到所有的有狀態業務對象。
讓我們來看一個簡單的例子,考慮一個封裝了業務邏輯的類的實現框架:
public class SomeBusinessClass extends
otherBusinessClass{
//核心數據成員
//其它數據成員:日誌流,保證數據完整性的標誌位等
//重載基類的方法
public void performSomeOperation
(OperationInformation info)
//安全性驗證
//檢查傳入數據是否滿足協議
//鎖定對象以食品店當其他線程訪問時的數據完整性
//檢查緩存中是否爲最新信息
//記錄操作開始執行時間
//執行核心操作
//記錄操作完成時間
//給對象解鎖
}
//一些類似操作
public void save(PersitanceStorage ps){
}
public void save(PersitanceStorage ps){
}
}
在上面的代碼中,我們注意到三個問題:首先,其它數據成員不是這個類的核心關注點;第二performSomeOperation()的實現做了許多核心操作之外的事,它要處理日誌、驗證、線程安全、協議驗證和緩存管理等一些外圍操作,而且這些外圍操作同樣也會應用於其他類;第三,save()和load()執行的持久化操作是否構成這個類的核心是不清楚的.
橫切關注點的問題
雖然橫切關注點會跨越多個模塊,但當前的技術傾向於使用一維的方法學來處理這種需求,把對應需求的實現強行限制在一維的空間裏.這個一維空間就是核心模塊級實現.其他需求的實現被嵌入在這個佔統治地位的空間.換句話說,需求空間是一個n維空間,而實現空間是一維空間,這種不匹配導致了糟糕的需求到實現的映射。
表現
用當前方法學實現橫切關注點是不好的.它會帶來一些問題,我們可以大致把這些問題分爲兩類。
代碼混亂:軟件系統中的模塊可能要同時兼顧幾個方面的需要.舉例來說,開發者經常要同時考慮業務邏輯、性能、同步,日誌和安全等問題,兼顧各方面的需要導致相應關注點的實現元素同時出現,引起代碼混亂.
代碼分散:由於橫切關注點本來就涉及到多個模塊.相關實現也就得遍佈在這些模塊裏.如在一個使用了數據庫的系統裏,性能問題就會影響所有訪問數據庫的模塊。這導致代碼分散在各處.
結果
混亂和分散的代碼會從多個方面影響系統的設計和開發:
可讀性差:同時實現幾個關注點模糊了不同關注點的實現,使得關注點與其實現之間的對應關係不明顯。
低產出:同時實現幾個關注點把開發人員的注意移到外圍關注點,導致生產效率降低.
低代碼重用率: 由於一個模塊實現多個關注點,因此其他需要類似功能的系統不能馬上使用該模塊.進一步降低了生產效率。
代碼質量差:混亂的代碼掩蓋了代碼中隱藏的問題。而且,由於同時要處理多個關注點.應該特別注意的關注點得不到應有的關注.
難以擴展:狹窄的視角和有限的資源總是產生僅注意當前關注點的設計.新的需求導致從新實現.由於實現不是模塊化的,就是說實現牽涉到多個模塊,爲了新需求修改子系統可能會帶來數據的不一致,而且還需相當規模同測試來保證這些修改不會帶來bug。
當前解決方法
由於多數系統中都包含橫切關注點、自然的已經形成了一些技術來模塊化橫切關注點的實現,這些技術包括:混入類、設計模式和麪向特定問題域的解決方式。
使用混入類,你可以推遲關注點的最終實現.基本類包含一個混入類的實例,允許系統的其他部分設置這個實例。舉個例子來說,實現業務邏輯的類包含一個混入的logger,系統的其他部分可以設置這個logger已得到合適的日誌類型,比如logger可能被設置爲使用文件系統或是消息中間件。在這種方式下,雖然日誌的具體實現被推遲,基本類還是必須在所有寫日誌的點調用日誌操作和控制日誌信息的代碼。
行爲型設計模式,如Visitor和Template Method模式,也允許你推遲具體實現。但是也就像混入類一樣,操作的控制——調用visitor或template Method的邏輯——仍然留給了基本類。
面向特定問題域的解決方式,如框架和應用服務器,允許開發者用更模塊化的方式處理某些橫切關注點。比如E J B(Enterprise JavaBean)架構,可以處理安全、系統管理、性能和容器管理的持久化(container-managed persistence)等橫切關注點。B e a n與數據庫的映射,但是大多數情況下,開發者還是要了解存儲結構。這種方式下,你用基於XML的映射關係描述器來實現於數據持久化相關的橫切關注點。
面向特定問題域的解決方式提供瞭解決特定問題的專門機制,它的缺點是對於每一種這樣的解決方式開發人員都必須重新學習,另外,由於這種方式是特定問題域相關的,屬於特定問題域之外的橫切關注點需要特殊的對待。
設計師的兩難局面
好的系統設計師不僅會考慮當前需求,還會考慮到可能會有的需求以避免到處打補丁。這樣就存在一個問題預知將來是很困難的,如果你漏過了將來可能會有的橫切關注點的需求,你將會需要修改或甚至是重新實現系統的許多部分;從另一個角度來說,太過於關注不一定需要的需求會導致過分設計的、難以理解的、臃腫的系統。所以系統設計師處在這麼一個兩難局面中:怎麼設計算不了過分設計?應該寧可設計不足還是寧可過分設計?
舉個例子來說,設計師是否應該在系統中包含現在並不需要的日誌機制?如果是的話,哪裏是應該寫日誌的點?日誌應該記錄那些信息相似的例子還有關於性能的優化問題,我們很少能預知瓶頸的所在。常用的方法是建立系統,profile它,然後翻新系統以提高性能,這種方式可能會依照profiling而修改系統的很多部分。此外,隨着時間的流逝,由於使用方式的變化,可能還會產生新的瓶頸。類庫設計師的任務更困難,因爲他很難設想出所有對類庫的使用方式。
總而言之,設計師很難顧及到系統可能需要處理的所有關注點。即使是在已經知道了需求的前提下,某些建立系統時需要的細節 也可能不能全部得到,整體設計就面臨着設計不足/過分設計的兩難局面。
AOP基礎
到目前爲止的討論說明模塊化橫切關注點是有好處的。研究人員已經嘗試了多種方法來實現這個任務,這些方法有一個共同的主題:分離關注點。A O P是這些方法中的一種,它的目的是清晰的分離關注點來解決以上提到的問題。
AOP, 從其本質上講,使你可以用一種鬆散耦合的方式來實現獨立的關注點,然後 組合這些實現來建立最終系統、用它所建立的系統是使用鬆散偶合的,模塊化實現的橫切關注點來搭建的、與之對照 用OOP建立的系統則是用鬆散耦合的模塊化實現的一般關注點來實現的。在AOP中,這些模塊化單元叫“方面(aspect)”,而在OOP中,這些一般關注點的實現單元叫做類。
AOP 包括三個清晰的開發步驟:
方面分解:分解需求撮出橫切關注點。在這一步裏,你把核心模塊級關注點和系統級的橫切關注點分離開來、就前面所提到的信用卡例子來說 你可以分解出三個關注點:核心的信用卡處理、日誌和驗證。
關注點實現:各自獨立的實現這些關注點,還用上面信用卡的例子,你要實現信用卡處理單元、日誌單元和驗證單元。
方面的重新組合:在這一步裏,方面集成器通過創建一個模塊單元—一方面來指定重組的規則, 重組過程—一也叫織入(weaving)或結合(integrating)——則使用這些信息來構建最終系統、還拿信用卡的那個例子來說 你可以指定(用某種AOP的實現所提供的語言)每個操作的開始和結束需要記錄,並且每個操作在涉及到業務邏輯之前必須通過驗證。

 


AOP與OOP最重要的不同在於它處理橫切關注點的方式.在AOP中 每個關注點的實現都不知道其它關注點是否會“關注”它,如信用卡處理模塊並不知道其它的關注點實現正在爲它做日誌和驗證操作。它展示了一個從OOP轉化來的強大的開發範型。
注意:一個AOP實現可以藉助其它編程範型作爲它的基礎,從而原封不動的保留其基礎範型的優點。例如,AOP可以選擇OOP作爲它的基礎範型,從而把OOP善於處理一股關注點的好處直接帶過來。用這樣一種實現,獨立的一般關注點可以使用OOP技術、這就像過程型語言是許多OOP語言的基礎一樣。
AOP語言剖析
就像其他編程範型的實現一樣,AOP的實現由兩部分組成:語言規範和實現。語言規範描述了語言的基礎單元和語法;語言實現則按照語言規範來驗證代碼的正確性,並把代碼轉成目標機器可執行的形式。這一節,我來解釋一下AOP組成部分。
AOP語言規範
從抽象的角度看來 一種AOP語言要說明下面兩個方面:
關注點的實現:把每個需求映射爲代碼,然後,編譯器把它翻譯成可執行代碼。由於關注點的實現以指定過程的形式出現,你可以使用傳統語言如C、C++、JAVA等。
織入規則規範:怎樣把獨立實現的關注點組合起來形成最終系統呢?爲了這個目的 需要建立一種語言來指定組合不同的實現單元,以形成最終系統的規則。這種指定織入規則的語言可以是實現語言的擴展,也可以是一種完全不同的語言。
AOP語言的實現
AOP的編譯器執行兩步操作:
l 組裝關注點
2 組裝結果轉成可執行代碼。
AOP實現可以用多種方式實現織入, 包括源碼到源碼的轉換、它預處理每個方面的源碼,產生織入過的源碼,然後把織入過的源碼交給基礎語言的編譯器, 產生最終可執行代碼。比如 使用這種方式,一個基於Java的AOP實現可以先把不同的方面轉化成Java源代碼,然後讓Java編譯器把它轉化成字節碼.也可以直接在字節碼級別執行織入; 畢竟 字節碼本身也是一種源碼。此外,底層的執行系統—一Java虛擬機—一也可以設計爲支持AOP的。基於Java的AOP實現如果使用這種方式的話,虛擬機可以先裝入織入規則,然後對後來裝入的類都應用這種規則、也就是說,它可以執行just-in-time的方面織入。
AOP的好處
AOP可幫助我們解決上面提到的代碼混亂和代碼分散所帶來的問題,它還有一些別的好處:
模塊化橫切關注點:AOP用最小的耦合處理每個關注點,使得即使是橫切關注點也是模塊化的。這樣的實現產生的系統,其代碼的冗餘小。模塊化的實現還使得系統容易理解和維護。
系統容易擴展:由於方面模塊根本不知道橫切關注點,所以很容易通過建立新的方面加入新的功能.另外,當你往系統中加入新的模塊時,已有的方面自動橫切進來,使系統易於擴展。
設計決定的遲綁定:還記得設計師的兩難局面嗎?使用AOP 設計師可以推遲爲將來的需求作決定,因爲他可以把這種需求作爲獨立的方面很容易地實現。
更好的代碼重用性:AOP把每個方面實現爲獨立的模塊, 模塊之間是鬆散耦合的.舉例來說,你可以用另外一個獨立的日誌寫入器方面來替換當前的,用於把日誌寫入數據庫,以滿足不同的日誌寫入要求。鬆散藕合的實現通常意味着更好的代碼重用性,AOP在使系統實現鬆散出合這一點上比OOP做得更好。
AspectJ:一個Java的AOP實現
AspectJ是一個可免費獲得的、由施樂公司帕洛阿爾託研究中心(Xerox PARC)開發的、Java的AOP實現。它使用java作爲單個關注點的實現語言,並擴展Java以指定織入規則.
另外,AspectJ允許以多種方式用方面和類建立新的方面,你可以引入新的數據成員和方法或是聲明一個新的類來繼承和實現另外的類或接口。
AspectJ的織入器——AspectJ的編譯器——負責把不同的方面組合在一起,由於AspectJ編譯器建立的最終系統是Java字節碼,因此,它可以運行在任何符合Java標準的虛擬機上。而且,AspectJ還提供了一些工具,例如調試器和Java IDE集成等。
我需要AOP嗎?
AOP僅僅是解決設計上的缺點嗎?在AOP裏,每個關注點的實現的並不知道是否有其它關注點關注它,這是AOP和OOP的主要區別。在AOP裏,組合的流向是從橫切關注點到主關注點,而OOP則相反。但是,OOP可以和AOP很好地共存。比如,你可以使用一個混入類來做組合,既可以用AOP實現,也可以用OOP實現,這取決你對AOP的接受程度,在這兩種情況下,實現橫切關注點的混入類實現都無需知道它自己是被用在類中還是被用在方面中。舉個例子來說,你可以把一個日誌寫入器接口用作某些類的混入類或是用作一個日誌方面,因而,從OOP到AOP 是漸進的。

瞭解AOP
在這篇文章裏,你看到了橫切關係帶來的問題,這些問題當前解決方法以及這些方法的缺點。你也看到了AOP 是怎樣克服這些缺點的,AOP的編程方式試圖模快化橫切關注點的實現,提供了一個更好更快的軟件開發方式。
如果你的系統中涉及到多個橫切關注點,你可以考慮進一步瞭解AOP,瞭解它的實現和它的好處。AOP很可能會是編程方式的一個里程碑。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/zl198183/archive/2005/07/01/409617.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章