從面向方面看軟件設(shè)計(jì)

0 評(píng)論 5582 瀏覽 3 收藏 15 分鐘

編輯導(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é)議。

更多精彩內(nèi)容,請(qǐng)關(guān)注人人都是產(chǎn)品經(jīng)理微信公眾號(hào)或下載App
評(píng)論
評(píng)論請(qǐng)登錄
  1. 目前還沒評(píng)論,等你發(fā)揮!