談談應用層切面設計
AOP概要理解糾偏
說到AOP,這個東東現在已經不是一個新詞匯了,我們拿一下百度詞條來看看是下面的樣子的:
在軟件業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。 |
但是今天我要說,這個定義太過狹義,實際上AOP的概念要比上面的定義要寬泛得多,今天我就給大家來扯扯AOP的事兒。
從AOP這三個單詞來看,它的定義還是非常準確的,就是面向切面編程。但是后面的這一堆解釋:
通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。 |
這明顯是鬼扯了,明顯是把一種具體的實現作為它定義,這就有點“白馬即馬”的意思了。
主要的變化就是把中間一段與具體的實現有關系的部分去掉。
通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。 |
這一段太過狹隘,已經遠遠不能滿足軟件技術發展的需要了,面向切面就面向切面,與什么預編譯的方式有毛關系?與運行期代理有毛關系?與Spring、函數式編程有毛關系?
因此,今天悠然就把AOP的概念重新縷縷:
在軟件業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過對某一領域進行高內聚低偶合方式進行實現,并通過非偶合的方式與其它業務邏輯進行整合,以對不同切面的實現邏輯進行隔離,從而使得不同領域的實現邏輯之間的耦合度降低,提高程序的可重用性、提高開發效率,同時也大大降低集成與整合難度的一種編程方法。 |
嗯嗯,雖然不一定嚴謹,但是比原來的廣泛多了,也更能體現AOP的真正意涵。它不僅包含原有定義里的模式,還包含各種各樣其它AOP模式,今天悠然就來給大家試著闡述一下。
AOP在軟件編程中的應用場景
傳統意義上的AOP
傳統意義上的AOP,是針對業務處理過程中的切面進行提取,它所面對的是處理過程中的某個步驟或階段,來獲得邏輯過程中各部分之間低耦合性的一些隔離效率,比如:常見的應用場景有:
- Authentication 權限
- Caching緩存
- Context passing內容傳遞
- Error handling 錯誤處理
- Lazy loading 延時加載
- Debugging 調試
- logging, tracing, profiling and monitoring 記錄跟蹤 優化 校準
- Performance optimization性能優化
- Persistence 持久化
- Resource pooling資源池
- Synchronization 同步
- Transactions事務
- 全文索引
- 等等
確實,AOP的應用場景有許多許多,也取得了良好的應用效果,這個已經經過了充分的實踐,許多書里也都進行了充分的闡述,這里不再贅述。
非傳統意義上的AOP
嗯嗯,今天的重點是這里,別人說得好的地方悠然說不過,因此最好的辦法就是不說,悠然說別人不說的地方,這樣就壓力小多了:)
以下針對@紅薯 的猜測純屬臆斷,如有雷同純屬巧合:
在正式開始之前,我們來舉個實踐場景:
@紅薯 的開源中國博客1.0
話說,在開源中國的博客列表中,每一篇博客是像下面一樣展示的:
從這一塊內容中,我們可以看到如下內容:
- 原創的圖標
- 置頂的圖標
- 博客的標題
- 博客的分類
- 博客的統計信息30評、2594閱、7贊
- 博客的編輯、刪除
我們從看到的這些信息里面,可以猜一下@紅薯 的表結果是怎么設計的:
表BLOG結構:
- 博客標識
- 博客標題
- 博客標題內容
- 是否原創
- 是否置頂
- 評論次數
- 閱讀次數
- 獲贊次數
- ....
當然這里應該也有一些其他的字段,我們上面的圖片里看不到,我們就當它們不存在。
我們再想想,估計@紅薯 還有詳細的記錄,比如:誰評論的,評論的內容是什么?誰閱讀的,是什么時候閱讀的?誰點贊的,是什么時候點贊的?
于是就有了下面的表:
評論詳情:
- 博客標識
- 評論者
- 評論時間
- 評論內容
- ...
閱讀詳情:
- 博客標識
- 閱讀者
- 閱讀時間
點贊詳情:
- 博客標識
- 點贊者
- 點贊時間
嗯嗯,@紅薯 仔細想了一想感覺差不多了的樣子,這個時候@紅薯 縷著他的頭發,點上一支煙進入了寫代碼的酸爽狀態。
比如,功能是這樣寫的:
//博客點贊 保存點贊(博客標識,點贊者標識){ 博客biz.添加點贊(博客標識,點贊者標識); } class 博客biz{ 添加點贊(博客標識,點贊者標識){ 博客dao.增加點贊數(博客標識); 點贊詳情dao.增加點贊詳情(博客標識,點贊者標識); } }
伴隨著一陣陣歡快的噼里啪啦鍵盤聲,開源中國博客1.0 OK了。
@紅薯 的開源中國新聞1.0
看著開源中國博客1.0的穩步運行,PV+UV雙豐收,@紅薯 縷著頭發咧著大板牙笑開了花,那就再整個開源中國新聞1.0唄!于是紅薯開始設計表結構:
表NEWS結構:
- 新聞標識
- 新聞標題
- 新聞標題內容
- 是否置頂
- 評論次數
- 閱讀次數
- 獲贊次數
- ....
當然,考慮到新聞也有點贊,也有評語,也有閱讀,于是下面的表結構也是需要的:
評論詳情:
- 新聞標識
- 評論者
- 評論時間
- 評論內容
- ...
閱讀詳情:
- 新聞標識
- 閱讀者
- 閱讀時間
點贊詳情:
- 新聞標識
- 點贊者
- 點贊時間
嗯嗯,紅薯看了下,尼瑪除了少個原創,別的都差不多的樣子么,但是不一樣還是不一樣的,那就再做一遍吧,于是一陣陣噼里啪啦之后又搞定了,紅薯抹著汗水,再仔細看一下代碼,尼瑪,怎么這兩邊的代碼幾乎一樣?感覺哪里有些不對的樣子。
管呢,先運行起來再說,哥去泡個妹子放松一下,于是紅薯打開了MAC,IPAD,IPHONE7S....
初級AOP方式
運行了一段時間,紅薯覺得要加個動態功能,發現要在兩邊都增加代碼,越來越覺得不對勁,于是拿起新買的《企業級JavaEE架構實踐》翻看起來,唉當時看書的時候不仔細,這個用切面來解決不是挺好的?
于是紅薯把表結構重構了一下:
評論詳情:
- 評論類型
- 評論對象標識
- 評論者
- 評論時間
- 評論內容
- ...
閱讀詳情:
- 閱讀類型
- 閱讀對象標識
- 閱讀者
- 閱讀時間
點贊詳情:
- 點贊類型
- 點贊對象標識
- 點贊者
- 點贊時間
另外增加一個動態表
- 動態類型
- 動態對象標識
- 動態者
- 動態時間
高級AOP方式
自從引入了切面功能,紅薯的工作明顯輕松多了,但是唯一不爽的是 隨著開源中國的用戶越來越多,投資人要求功能方面也要快速推進,現在的紅薯幾乎已經沒有可以縷的頭發了,投資人要增加日歷、要增加勛章、要增加TAG、要增加提醒,要增加的東西越來越多,同時開源中國的訪問者們的眼界又越來越寬了,要求越來越高了,今天這個做的丑了,明天那個做得不人性化了, 紅薯開發、測試、發布、切面配置、界面調整、控制層啥都要做,這不又出問題了:
紅薯的手習慣的抬起來縷頭發了,但是隨著OSC功能的增加,頭發已經幾乎找不到了,紅薯狠狠的把煙屁股擰進煙灰缸,是要徹底解決這個問題了,看起來還得讀書啊,再翻翻《企業級JavaEE架構實踐》,看看有沒有啥思路。
Think Big, Start small , Scale Fast. |
好的軟件是品出來的。 |
仔細看了幾遍,紅薯的眼睛忽然亮了起來,只要把所有的評論、點贊、評論、動態、以及其它雜7雜8的東西都讓它高內聚,低耦合,我做博客只管做博客、做新聞只管做新聞,哪怕再做他多少個,也是僅僅關注我要做的東西,而把這些東西都做成一個個的切面讓這些切面去完整的解決這些問題,這樣我不就輕松了?原來雖然也引入了AOP技術,但是由于只是初級切面,所以只解決了部分問題,并沒有徹底的解決問題。如果我能把切面也切到控制層、界面層,這樣就可以真正的做到高內聚、松耦合,而且是真正的業務切面。
技術的切面還是初級的,業務層級的切面才是終級的。
具體實現起來,還是沒有什么思路,聽聽悠然有沒有什么思路?
終級解決方案
通過上面對開源中國兩個功能模塊的分析,我們大致清楚了問題的癥結所在,并通過對紅薯的心路歷程進行分析,大致引入了問題的解決思路,但是真正的落地確不是那么簡單的。一個問題的抽象的程度越高,其通用性越好,但是它的易理解程度和易實現程度也就越難。我對實現者面對面多次溝通這個思路時,更多的時候看到的是他們茫然的眼神,以及多次感覺理解了,但是真正實現的時候卻又達不到要求,直到許多次反復之后,才慢慢進入狀態。
所以我會盡力講清楚,但是不能保證你一定能理解到我內心所想。
要解決的問題
我們在應用開發過程中,一部分是與具體的業務相關的,另外一部分它與具體的業務并不相關,但是在業務過程中又是有這樣那樣關系的。如果我們能把這些與具體業務沒有直接關系,但又是對業務數據的一種維度的補充或描述能獨立出來,想用或不想用僅在一念間,那么這個時候做軟件就是一種享受了,而且業務和切面都可以獨立變化,而不必考慮彼此的影響。
這個時候從開發效率來說,由于大家各自高內聚低耦合,所以開發、測試、驗證、發布都會方便快速許多,集成的時候由于也不必有配置和代碼及界面上的協作和引用,這樣就可以大大的降低系統集成過程中的開發、測試工作,也避免了由此導致的一系列的可能出現的問題。
那么接下來,我們就用上面分析問題時的例子來闡述,如何解決這個問題,整個實現思路采用Tiny框架相關技術進行解決,非TINY技術平臺或框架也是可以實現的,當然實現細節可能會有不同,需要相關的架構師進行相應的調整。
解決問題的思路
每個切面的內容都在自己的業務單元中實現,同時要能在能出來的時候它就能出來。
這里我們就拿上面說的一個場景"評論"來示例
評論詳情:
- 評論類型
- 評論對象標識
- 評論者
- 評論時間
- 評論內容
既然是高內聚低耦合,那當然主是要統一界面的樣式。
評價的使用場景一般來說是在顯示完主要業務內容之后(如:博客、新聞、文檔、軟件、etc),可以進行評論,可以進行回復,可以進行引入,當然也要能把已經評論的內容顯示出來。
但是需要搞清楚的是,這些內容的展現與人機交互過程明顯與不同的業務內容沒有一點關系,這也就為進行切面提供了基礎。為了便于進行說明問題,這里采用AJAX模式進行解釋,雖然采用非AJAX方式也是可以解釋得通的,但是應用到的技術的復雜度與解釋起來的困難都非常更加復雜。
舉個例子,做出來的效果是類似下面的樣子:
這個時候,問題就轉換為,如何把當前要處理的業務類型和業務數據的標識讓評論部分知曉,這個也非常簡單,只要用下面的樣子寫一段模板即可:
#comment("BLOG",blog.blogId)上面的模板語言是指:現在我是在給一個BLOG類型的業務對象做評論,業務對象的標識是blog對象的blogId屬性。
嗯嗯,這個時候較以前的開發,已經省了非常大的工作量了,只是簡單的聲明一下就可以了。
更進一步的思考
雖然上面的思路已經比較好的解決了問題,但是這里有一個問題:那就是實現時序問題。也就是說,博客功能是紅薯先加的,而評論功能是紅薯后加的。這里就出現一個問題:為什么后增加的功能,需要我在已經實現的妥妥的功能要進行調整??這豈不是說,你后面不斷的增加功能,我這里都要不斷的進行聲明?雖然也有一定的道理,但是是不符合好萊塢原則的。實際上也是違反常理的===我是先來的,為什么要為你一個后到的做調整?我根本就不知道你的存在好么?!
出于我們對完美架構的追求以前我們不將就的態度,我們當然要更好的解決此問題。
我們可以在應用當中預設一些擴展點,但是具體擴展些什么東西我是不管的。
這個時候,我們就可以只在界面中增加如下的一段模板語言:
#extendPoint("VIEW","BLOG",blog.blogId)
上面的腳本表示,我這里是一個VIEW類型的擴展點,業務類型是BLOG,業務對象標識是blog對象的blogId屬性。
然后由extendPoint決定往里面塞哪些東西。
這個時候,我只要在評論工程里增加如下的配置:
<extend-points> <extend-point type="page" order="10"> #comment(bizType,bizId) </extend-point> </extend-points>然后如果這個時候,我們又要增加一個留腳印的功能
我們只要增加一個留腳本的功能模塊,在這個模塊中增加下面的配置:
<extend-points> <extend-point type="page" order="9"> #footprint(bizType,bizId) </extend-point> </extend-points>
總結
通過上面的改進,紅薯同學再也不用薅頭發了,原有的業務代碼再也不用動,這功能居然自動在所有功能中自動出現了。
想到這里,悠然就深深的自責,早點給紅薯出主意,紅薯同學的一頭黑發就留下來了。
當然,上面只是說了一個思想,真正的實現的時候用到的技術還是非常多的:- 模塊化
- AOP
- SOA(如果是互聯網應用是需要的,普通的小工程可以不用)
- 模板語言或類似技術
如果本人沒有說清楚,或者讀者朋友們沒有理解,本人深感自責,也希望同學們能夠理解,并在下面的回復博客提問,本人定盡力給出解釋。
有喜歡本人博客內容的同學請加關注,以便及時獲知本人精品內容!