從面向方面看軟件設(shè)計(jì)
編輯導(dǎo)語(yǔ):面向方面編程也就是AOP,它使開發(fā)人員可以更好地將本不該彼此糾纏在一起的任務(wù)(例如數(shù)學(xué)運(yùn)算和異常處理)分離開來(lái);本文作者從面向方面看軟件設(shè)計(jì),我們一起來(lái)了解一下。
產(chǎn)品小王今天接到了一個(gè)新的需求——客戶希望把登錄驗(yàn)證時(shí)間從12小時(shí)調(diào)整到24小時(shí)。
產(chǎn)品小王接到需求時(shí),心想這不就是把服務(wù)器中的數(shù)字從12改成24嗎?簡(jiǎn)單得很,所以為了展現(xiàn)自己的專業(yè)性,他一口答應(yīng)市場(chǎng)部的同事說(shuō)今天就可以實(shí)現(xiàn)你這個(gè)需求;心里還想著用一點(diǎn)小手段就讓市場(chǎng)部同事欠自己一個(gè)人情,以后找市場(chǎng)部辦事方便多了。
于是產(chǎn)品小王便找到研發(fā)老王說(shuō):能不能幫忙把登錄驗(yàn)證時(shí)間改成24小時(shí)啊,我已經(jīng)答應(yīng)市場(chǎng)部了今天實(shí)現(xiàn),沒問題吧。
研發(fā)老王一聽就說(shuō)這個(gè)做不了,你趕緊和研發(fā)部同事說(shuō)下,別耽誤人家事了。
小王一聽,急了,急忙問到為什么啊,不就是改個(gè)數(shù)字嗎?
老王說(shuō),可不僅僅是改個(gè)數(shù)字,我們當(dāng)時(shí)在實(shí)現(xiàn)系統(tǒng)的時(shí)候,為了快速實(shí)現(xiàn)安全的相關(guān)功能,所以安全模塊是貫穿很多模塊的;如果要改這個(gè)數(shù)字,首先要看在各個(gè)模塊中關(guān)于安全模塊的相關(guān)代碼,然后再查看修改代碼產(chǎn)生的影響,最后還要測(cè)試數(shù)字修改后代碼是否能正常運(yùn)行。
小王心想:為什么改個(gè)數(shù)字要這么復(fù)雜???
其實(shí)這個(gè)問題在軟件工程中很早就出現(xiàn)了,軟件工程中也早就有一個(gè)很好的解決辦法——面向方面編程(AOSE:Aspect-Oriented Software Engineering),這篇文章就詳細(xì)展開說(shuō)明什么是面向方面編程。
一、面向及面向方面
一般情況下,在軟件實(shí)現(xiàn)過(guò)程中,單個(gè)需求需要多個(gè)組件實(shí)現(xiàn),而每個(gè)組件也可能同時(shí)服務(wù)于多個(gè)需求。
換句話說(shuō)就是一個(gè)組件可以服務(wù)多個(gè)需求,一個(gè)組件中也包含實(shí)現(xiàn)多個(gè)系統(tǒng)需求的代碼;正如在下面這張圖片中,安全需求組件和恢復(fù)需求組件同時(shí)服務(wù)于客戶需求,賬戶需求和管理需求,組件之間相互搭配,進(jìn)而實(shí)現(xiàn)系統(tǒng)功能的。
軟件系統(tǒng)結(jié)構(gòu)
在圖中可以看到,在這個(gè)簡(jiǎn)單的系統(tǒng)中有三個(gè)核心功能組件,分別是客戶需求,賬戶需求和管理需求;同時(shí)為了保持這三個(gè)核心功能組件能穩(wěn)定運(yùn)行,增加了安全需求和恢復(fù)需求組件。
在面向方面編程中,核心關(guān)注點(diǎn)(Concerns)指系統(tǒng)要實(shí)現(xiàn)的主要功能,比如上述圖片中的客戶需求、賬戶需求、管理需求;而把服務(wù)于核心關(guān)注點(diǎn)實(shí)現(xiàn)的功能稱為橫切關(guān)注點(diǎn)(CrossCutting Concerns),比如上述圖片中的中的安全需求和恢復(fù)需求。
傳統(tǒng)的代碼實(shí)現(xiàn)過(guò)程中,核心關(guān)注點(diǎn)的實(shí)現(xiàn)總是包含額外的代碼來(lái)實(shí)現(xiàn)橫切關(guān)注點(diǎn),這就會(huì)導(dǎo)致代碼混亂和分散;雖然這種程序的實(shí)現(xiàn)方式能夠提高效率,但是這種結(jié)構(gòu)會(huì)導(dǎo)致的橫切關(guān)注點(diǎn)的組件修改成本,復(fù)用成本都非常高。
原因是需要找出橫切關(guān)注點(diǎn)與核心關(guān)注點(diǎn)組件間如何相互配合,并評(píng)估修改后對(duì)核心關(guān)注點(diǎn)組件的影響,修改完成后,還要全部驗(yàn)證核心關(guān)注點(diǎn)的組件。
說(shuō)到這里,已經(jīng)說(shuō)明清楚面向方面編程的起因,接下來(lái)將說(shuō)明什么是方面以及什么是面向方面編程。
方面指的就實(shí)現(xiàn)一個(gè)功能的程序,與其他程序不同的是,方面更偏向于描述程序間組成方法——一個(gè)可執(zhí)行的方面根據(jù)自身的描述去組合對(duì)象,方法和其他方面創(chuàng)建處理的,同時(shí)規(guī)定了程序在什么地方運(yùn)行。
方面的主要內(nèi)容包括切入點(diǎn)、程序和連接點(diǎn):切入點(diǎn)說(shuō)明約定方面在什么時(shí)間開始執(zhí)行程序;連接點(diǎn)指定系統(tǒng)在執(zhí)行完程序后繼續(xù)執(zhí)行的程序,包括進(jìn)行方法調(diào)用、初始化變量或者更新域,定義引用的事件集合等,如下圖所示。
面向方面編程實(shí)例
面向方面編程正是基于方面的概念而誕生的,是一種專門實(shí)現(xiàn)橫向關(guān)注點(diǎn)組件的編程思想。
二、分離關(guān)注點(diǎn)
面向方面編程的核心內(nèi)容是分離關(guān)注點(diǎn),是思考和構(gòu)建軟件系統(tǒng)的重要方法。
在面向方面編程中將關(guān)注點(diǎn)劃分為各自獨(dú)立的關(guān)注點(diǎn),要求程序中的每個(gè)方面(類、方法、過(guò)程等)只為實(shí)現(xiàn)一個(gè)目的,進(jìn)而降低修改和復(fù)用方面的成本,甚至不用思考關(guān)注點(diǎn)之間的相互影響。
當(dāng)用關(guān)注點(diǎn)來(lái)表示一個(gè)需求或者一組需求的時(shí)候,我們可以很容易在實(shí)現(xiàn)組件中跟蹤需求;如果需求發(fā)生改變,研發(fā)人員可以快速定位到需要需改的代碼,并且不需要考慮方面之間的相互影響,快速實(shí)現(xiàn)需求改變。
三、實(shí)現(xiàn)面向方面編程
在面向方面編程中,關(guān)注點(diǎn)是從系統(tǒng)需求中導(dǎo)出的,利用分離關(guān)注點(diǎn)的概念作為考慮需求和設(shè)計(jì)系統(tǒng)的基礎(chǔ);這個(gè)是進(jìn)行軟件設(shè)計(jì)的原則,下文中展開的軟件設(shè)計(jì)的步驟也是基于此進(jìn)行描述。
1. 明確軟件需求
和大部分的軟件設(shè)計(jì)一樣,在進(jìn)行軟件設(shè)計(jì)時(shí)要最先明確軟件需求,也就是明確軟件的主要功能是什么;只有抓住了主要功能,才能保證我們?cè)谲浖O(shè)計(jì)過(guò)程中不會(huì)偏離方向。
2. 核心系統(tǒng)設(shè)計(jì)
明確軟件需求后,我們就可以通過(guò)軟件需求推導(dǎo)出核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)。
3. 方面識(shí)別和設(shè)計(jì)
通過(guò)視點(diǎn)識(shí)別方面是一種最常用的方法。如下圖所示,每個(gè)視點(diǎn)代表一種用戶的一組關(guān)注點(diǎn),而每組關(guān)注點(diǎn)又可以分為核心關(guān)注點(diǎn)系統(tǒng)和橫切關(guān)注點(diǎn)。
通過(guò)視點(diǎn)識(shí)別關(guān)注點(diǎn),能很好地保證我們?cè)诜治鰰r(shí)不重復(fù)、不遺漏,盡可能找到所有的關(guān)注點(diǎn)。
面向方面的設(shè)計(jì)和編程
面向方面的設(shè)計(jì)是利用方面進(jìn)行系統(tǒng)設(shè)計(jì)的過(guò)程,通過(guò)方面來(lái)實(shí)現(xiàn)那些在需求工程中所找出來(lái)的橫切關(guān)注點(diǎn),同時(shí)將方面與系統(tǒng)的其他組件組合在一起。
4. 沖突分析和解決
在將方面和系統(tǒng)的其他組件結(jié)合在一起時(shí),要分析并解決可能存在的沖突,保證不發(fā)生組合的二義性;在不保證二義性的情況下,要找到每個(gè)方面與系統(tǒng)合適的切入點(diǎn);只有找到了合適的切入點(diǎn),方面運(yùn)行才會(huì)符合設(shè)計(jì)者的需求。
需要特別注意的是,由于方面都是基于對(duì)系統(tǒng)一定的預(yù)期而獨(dú)立設(shè)計(jì)的,當(dāng)多個(gè)方面一起作用與一個(gè)系統(tǒng),一個(gè)方面對(duì)系統(tǒng)的影響會(huì)導(dǎo)致其他方面運(yùn)行失敗。
5. 名字設(shè)計(jì)
面向設(shè)計(jì)的最后一步是方面及切入點(diǎn)的名字設(shè)計(jì),由于面向方面特殊的軟件執(zhí)行順序,所以在設(shè)計(jì)面向方面程序時(shí)要特別注意方面和切入點(diǎn)的名字設(shè)計(jì),避免方面通過(guò)切入點(diǎn)執(zhí)行程序時(shí)調(diào)用錯(cuò)誤。
6. 總結(jié)
面向方面編程的流程圖如下:
面向方面的設(shè)計(jì)過(guò)程的流程圖
四、測(cè)試
由于面向方面編程代碼的特殊結(jié)構(gòu),測(cè)試是具有一定困難的,主要的原因是方面的程序與主體代碼是緊湊的,而不是松散的;就算測(cè)試后在一處能正常工作,在其他環(huán)境下也未必能正常工作。
下面展開說(shuō)明可能的測(cè)試方法和遇到的困難:
1. 閱讀代碼測(cè)試
面向方面編程的代碼之間通過(guò)切入點(diǎn)進(jìn)行連接,導(dǎo)致代碼無(wú)法直接閱讀;雖然可以借助一些代碼閱讀工具可以使代碼“變平”,從而降低代碼的閱讀難度,但是面向方面編程的程序語(yǔ)言本身是動(dòng)態(tài)的,而非靜態(tài)的;所以借助工具閱讀代碼的只是解決了表面問題,無(wú)法解決實(shí)際問題。
2. 白盒測(cè)試
與白盒類似的還有結(jié)構(gòu)化測(cè)試,這兩種測(cè)試方法的共同點(diǎn)是設(shè)計(jì)能提供一定程度覆蓋的測(cè)試方法;比如保證軟件的每個(gè)執(zhí)行路徑都執(zhí)行過(guò)一遍,每個(gè)程序語(yǔ)句都執(zhí)行過(guò)一次。
遇到的問題和閱讀代碼測(cè)試類似,由于面向編程軟件通過(guò)切入點(diǎn)連接,導(dǎo)致面向方面的程序不是結(jié)構(gòu)化程序;在某些運(yùn)行環(huán)境下,一些方面可能被執(zhí)行,一些方面不執(zhí)行;而在另一種運(yùn)行環(huán)境下,方面的執(zhí)行情況又是另一種情況,方面之間又相互影響;所以產(chǎn)生了龐大的測(cè)試地圖,大大增加了測(cè)試難度。
很多現(xiàn)實(shí)世界中的系統(tǒng)都會(huì)涉及敏感操作,為了減少敏感操作帶來(lái)的損失,系統(tǒng)需要在敏感操作之前驗(yàn)證用戶身份,并進(jìn)行相關(guān)的記錄操作,比如生成日志、發(fā)出通知等。
那么這種功能的實(shí)現(xiàn)邏輯是什么呢?這篇文章將以財(cái)務(wù)系統(tǒng)的數(shù)據(jù)查看功能為例子說(shuō)明面向方面編程如何實(shí)現(xiàn)用戶認(rèn)證。
財(cái)務(wù)系統(tǒng)中有很多敏感數(shù)據(jù)及敏感操作權(quán)限,這些數(shù)據(jù)及操作只能被有相關(guān)權(quán)限的員工看到及使用。
但是在很多情況下,僅僅靠權(quán)限控制無(wú)法達(dá)到理想效果,比如財(cái)務(wù)人員打開了相關(guān)頁(yè)面并短暫離開座位去完成其他工作,這個(gè)時(shí)候該頁(yè)面上的數(shù)據(jù)和操作沒有任何保護(hù)措施。
為了對(duì)敏感數(shù)據(jù)及操作有更好的保護(hù)效果,產(chǎn)品往往會(huì)在相關(guān)領(lǐng)域增加賬號(hào)驗(yàn)證——要查看敏感數(shù)據(jù)或者進(jìn)行敏感操作需要先驗(yàn)證賬號(hào)密碼;如果密碼不匹配,將直接退出系統(tǒng)并記錄相關(guān)操作信息;如果賬號(hào)密碼正確,則繼續(xù)操作并記錄相關(guān)操作信息。
這種功能的實(shí)現(xiàn)邏輯大概有以下兩種:
- 修改每個(gè)敏感數(shù)據(jù)及操作的組件,讓組件去調(diào)用賬號(hào)密碼驗(yàn)證并生成操作信息;
- 修改系統(tǒng)代碼,使得每次進(jìn)行敏感操作的時(shí)候都調(diào)用賬號(hào)密碼認(rèn)證,在認(rèn)證完成后記錄相關(guān)信息;
以上兩種實(shí)現(xiàn)邏輯都不是非常合理的,原因如下:
- 第一種邏輯的問題是會(huì)導(dǎo)致驗(yàn)證操作和記錄操作綁定在一起,不但造成代碼冗余而且會(huì)固化代碼,降低代碼的靈活性;
- 第二種邏輯的問題是將代碼分散操作,會(huì)導(dǎo)致后續(xù)修改需求時(shí)無(wú)法快速定位對(duì)應(yīng)的代碼,同時(shí)也會(huì)固化代碼,降低代碼的靈活性;
其實(shí)不難看出,賬號(hào)密碼驗(yàn)證及操作是屬于橫切關(guān)注點(diǎn),可以使用面向方面編程實(shí)現(xiàn),具體如下:
- 切入點(diǎn):查看敏感數(shù)據(jù)或者進(jìn)行敏感操作;
- 切入點(diǎn)程序:進(jìn)行賬號(hào)密碼驗(yàn)證操作;
- 連接點(diǎn)程序:記錄操作的相關(guān)信息等;
代碼結(jié)構(gòu)如下圖所示:
敏感操作的面向方面編程結(jié)構(gòu)圖
從上例中可以看到,面向方面編程的優(yōu)點(diǎn)是代碼冗余少且靈活;但是在一定程度上增加了代碼的復(fù)雜度,可能會(huì)出現(xiàn)無(wú)法預(yù)知的問題。
面向方面編程并沒有大范圍使用,從這個(gè)意義上來(lái)說(shuō),面向方面編程對(duì)編寫代碼的意義不大,面向方面編程更多的意義在于系統(tǒng)架構(gòu);通過(guò)面向方面思想設(shè)計(jì)的架構(gòu)能有更強(qiáng)的靈活性和更少的冗余,這也是個(gè)人了解完面向方面后的最大感受。
作者:寶寶心里苦??;公眾號(hào):寶寶心里苦啊
本文由 @寶寶心里苦啊 原創(chuàng)發(fā)布于人人都是產(chǎn)品經(jīng)理,未經(jīng)作者許可,禁止轉(zhuǎn)載。
題圖來(lái)自Unsplash,基于CC0協(xié)議。
- 目前還沒評(píng)論,等你發(fā)揮!