架構(gòu)漫談系列(1) 關(guān)注點(diǎn)分離

很想寫(xiě)相關(guān)的內(nèi)容,一直以來(lái)這方面的東西很雜,自己各方面都多多少少有些總結(jié),但是沒(méi)有系統(tǒng)的成文,始終覺(jué)得是個(gè)遺憾。
這是這個(gè)系列的第一篇。
本文說(shuō)的架構(gòu),還并不是說(shuō)的Tier層的架構(gòu),這里面不會(huì)涉及到分布式、緩存、網(wǎng)絡(luò)結(jié)構(gòu)等等的布局,而是集中在軟件的內(nèi)部,是代碼層級(jí)的,考慮這點(diǎn)架構(gòu)的點(diǎn),目的是在于幫助我們寫(xiě)出清晰、易維護(hù)的軟件。

關(guān)注點(diǎn)分離(Separation of concerns, SoC)

這個(gè)準(zhǔn)則應(yīng)該作為我們開(kāi)發(fā)和架構(gòu)的指導(dǎo)性的原則。在該原則下,軟件應(yīng)該按照其業(yè)務(wù)來(lái)將軟件本身劃分成不同的部分,從而進(jìn)一步降低耦合性,不過(guò),這感覺(jué)是句廢話,大家好像都懂。

那么首先,關(guān)注點(diǎn)是什么呢?

比如說(shuō)一組對(duì)代碼有影響的業(yè)務(wù)邏輯,或?qū)δ硞€(gè)具體業(yè)務(wù)有影響的業(yè)務(wù)規(guī)則。它其實(shí)可以很通用,比如針對(duì)x86環(huán)境優(yōu)化代碼的細(xì)節(jié);也可以很具體,比如某個(gè)將要初始化的類(lèi)的名字,只要它對(duì)我們是有用的,我們就稱(chēng)它為其中的一個(gè)關(guān)注點(diǎn)。

舉例來(lái)說(shuō),如果某個(gè)軟件有個(gè)邏輯:是將某些產(chǎn)品高亮顯示出來(lái),以顯示這些產(chǎn)品的獨(dú)特性。
那么,把這些產(chǎn)品挑選出來(lái)的邏輯,應(yīng)該和把這些產(chǎn)品做高亮的邏輯分離開(kāi)來(lái),這是兩個(gè)不同的關(guān)注點(diǎn)(只是剛好這兩個(gè)關(guān)注點(diǎn)是互相關(guān)聯(lián)的而已)。

在架構(gòu)上,如何去應(yīng)用這條準(zhǔn)則呢?比如說(shuō),把業(yè)務(wù)邏輯的行為分成基本的實(shí)現(xiàn)層(infrastruture)和UI層(理想的情況下,業(yè)務(wù)規(guī)則和業(yè)務(wù)邏輯都應(yīng)該分離到不同的項(xiàng)目里面去,他們也不能互相產(chǎn)生依賴(lài)的關(guān)系)。這種結(jié)構(gòu)能幫助我們保證業(yè)務(wù)邏輯更容易的測(cè)試和應(yīng)用,而且在底層也沒(méi)有互相耦合在一起。
關(guān)注點(diǎn)分離是我們對(duì)于軟件分層的一個(gè)核心的考慮點(diǎn)。

把握好這個(gè)尺度,有助于我們建造模塊化的應(yīng)用程序。它的價(jià)值在于簡(jiǎn)化開(kāi)發(fā)和提高維護(hù)性。這個(gè)準(zhǔn)則做好了,各獨(dú)立部分就能重用,也可以相對(duì)獨(dú)立的開(kāi)發(fā)和更新,某個(gè)模塊更新了,其他的模塊不必做額外的修改。

但是,聽(tīng)起來(lái)還是好抽象的感覺(jué)。

舉例來(lái)說(shuō),ASP.NET MVC就是關(guān)注點(diǎn)分離的一個(gè)體現(xiàn),它將原來(lái)的ASP.NET WebForm分離成模型(model)-視圖(view)-控制器(controller),從而把業(yè)務(wù)邏輯、數(shù)據(jù)、界面分離,這也是組織代碼結(jié)構(gòu)的一個(gè)形式。

MVC的基本結(jié)構(gòu):

  • Model層表示應(yīng)用程序的數(shù)據(jù)核心,通常負(fù)責(zé)在數(shù)據(jù)庫(kù)中存取數(shù)據(jù)。

  • View是應(yīng)用程序的顯示層,通常是依據(jù)模型的數(shù)據(jù)而建立。

  • Controller是用來(lái)控制和處理輸入輸出的,是處理用戶交互的部分,也負(fù)責(zé)向模型(Model層)發(fā)送數(shù)據(jù)。

    MVC的這個(gè)設(shè)計(jì)各個(gè)關(guān)注點(diǎn)是分開(kāi)的,這樣有助于我們管理和開(kāi)發(fā)復(fù)雜的應(yīng)用程序,我們可以在某個(gè)時(shí)間點(diǎn)只集中精力在其中的某一個(gè)關(guān)注點(diǎn),而不是所有的部分。舉例來(lái)說(shuō),前端的開(kāi)發(fā)人員可以配合設(shè)計(jì)團(tuán)隊(duì)繞過(guò)業(yè)務(wù)邏輯,專(zhuān)注在視圖和交互設(shè)計(jì)部分。另外的一端,DBA也可以配合某個(gè)團(tuán)隊(duì)專(zhuān)注在數(shù)據(jù)持久化的部分,而中間的業(yè)務(wù)邏輯層又可以由其他團(tuán)隊(duì)集中精力來(lái)負(fù)責(zé)。這種分層也簡(jiǎn)化了分組開(kāi)發(fā),讓測(cè)試也更為容易。

除了ASP.NET MVC還有其他的框架也是這樣的關(guān)注點(diǎn)分離的思想,比如Django,Structs,Spring等等。

那么分層思想都有哪些方法呢,總不至于只是用個(gè)MVC就結(jié)束了吧?

其實(shí)東西還是比較多的,下面,將介紹一些分層的思想。

縱向分離

大家都懂,即便是最初級(jí)的程序員也都接觸過(guò),可能是你并沒(méi)有意識(shí)到而已。
我們十好幾年前的三層架構(gòu),界面層(UI Layer),業(yè)務(wù)邏輯層(Business Layer)和數(shù)據(jù)持久化層(Data Access Layer),就是這一種自上而下的縱向的分層手法。

橫向分離

大家也懂。我們倡導(dǎo)的模塊化的編程,把我們的軟件拆分成模塊或子系統(tǒng)。
從左到右是模塊1、模塊2、模塊3,這是一種水平方向的切割。
這跟縱向的分離是兩個(gè)不同的方向,橫向分離大多是模塊化的過(guò)程。

切面分離

有些內(nèi)容是多個(gè)層之間都需要的,比如日志(logging),在你的系統(tǒng)里面,界面層、邏輯層、數(shù)據(jù)訪問(wèn)層可能都需要寫(xiě)日志,這種跨到多層同樣邏輯就可以考慮切面分離。
在asp.net mvc中,我們可以使用filter來(lái)實(shí)現(xiàn), Spring中也有SpringAOP等等。

依賴(lài)方向分離

我們考慮這幾點(diǎn):

  • 有些類(lèi)要修改的幾率比其他的類(lèi)修改的幾率大得多。
  • 具體的類(lèi)比抽象類(lèi)修改的幾率大得多。
  • 修改被依賴(lài)得很多的類(lèi)可能引起很大的改動(dòng)。
  • 某些類(lèi)比其他類(lèi)被重用的可能性大得多。

依據(jù)這些考慮點(diǎn),我們來(lái)決定某個(gè)類(lèi)應(yīng)該放在哪個(gè)層次里面,或者考慮將某一層切割成多層。

關(guān)注數(shù)據(jù)分離

在組織數(shù)據(jù)時(shí),應(yīng)該盡量考慮數(shù)據(jù)本身的固有屬性,如果不是它們的固有屬性,那么應(yīng)該分離出來(lái)。

比如產(chǎn)品的類(lèi)就不應(yīng)該關(guān)聯(lián)customer類(lèi),因?yàn)楫a(chǎn)品不應(yīng)該跟客戶直接產(chǎn)生數(shù)據(jù)關(guān)系,產(chǎn)品的顏色、型號(hào)、描述才是產(chǎn)品該有的固有屬性。

至于客戶,應(yīng)該是用訂單類(lèi)來(lái)把他們聯(lián)系在一起。

關(guān)注行為分離

跟上面講的一樣,行為也應(yīng)該是事物或?qū)ο蟮墓逃械谋旧淼男袨椋黠@偏離原來(lái)行為的,應(yīng)該考慮成另外的關(guān)注點(diǎn)兒分離開(kāi)。

比如有一個(gè)函數(shù)叫做CreateNewCustomer(),那么CreateNewCustomer的行為就應(yīng)該限定在創(chuàng)建一個(gè)新客戶上面,給新客戶自動(dòng)發(fā)優(yōu)惠券的動(dòng)作就不能放到這個(gè)函數(shù)里面。

擴(kuò)展分離

如果基于某種設(shè)計(jì),原先不具有某些行為需要增加,可以考慮通過(guò)擴(kuò)展或插件的形式來(lái)完成,將這些功能放入到插件或擴(kuò)展中,就是擴(kuò)展分離。

比如Firefox、Chrome的去廣告的插件,這些功能增加了系統(tǒng)原本的行為,將這些行為分離到插件里面去,就是擴(kuò)展分離。

委托分離

如果某個(gè)行為還無(wú)法具體確定,可以使用委托的方式。
比如C#的delegate,當(dāng)我們還不知道某些具體行為應(yīng)該如何實(shí)現(xiàn),或者不應(yīng)該在此處對(duì)該行為進(jìn)行實(shí)現(xiàn),或者有多個(gè)行為可以互相替代,就可以將函數(shù)的參數(shù)指定為一個(gè)delegate。

至于delegate具體怎樣實(shí)現(xiàn),那是其他部分應(yīng)該關(guān)注的點(diǎn)。

比如現(xiàn)在需要將Customer的信息持久化,就可以把這個(gè)請(qǐng)求委托給DatabaseManager或WebSerivceManager,由他們自行處理數(shù)據(jù),然后返回給我結(jié)果。

反轉(zhuǎn)分離

現(xiàn)在有了很多的依賴(lài)注入的框架,像Autofac,Unit,Castle Windsor等等,這些幫助我們做依賴(lài)翻轉(zhuǎn),從而倒置依賴(lài)關(guān)系。

要指出是,上面提到了9種分離層次的概念,每一種概念都可以任意的與其他概念組合在一起,從而產(chǎn)生更多的變化。

在實(shí)際的開(kāi)發(fā)過(guò)程中,沒(méi)有東西是一成不變的,而層次和架構(gòu)也應(yīng)該是在開(kāi)發(fā)的過(guò)程里面不斷完善和重構(gòu)。

初級(jí)程序員最煩的是需求或業(yè)務(wù)的修改,一些我們覺(jué)得奇奇怪怪的修改導(dǎo)致大家不斷的修改代碼,心里很煩,在心里也默默的把產(chǎn)品經(jīng)理被翻過(guò)來(lái)倒過(guò)去罵了千百遍。

但是,在實(shí)際的工作中你會(huì)發(fā)現(xiàn),軟件開(kāi)發(fā)就是這樣,沒(méi)有什么是不變的。

如果一定要找出一個(gè)不變的點(diǎn),我想那應(yīng)該是:

唯一不變的,就是變化。

關(guān)于不斷的修改與重構(gòu),也可以參考《重構(gòu)-改善既有代碼的設(shè)計(jì)》,記得封面上寫(xiě)的是:

軟件開(kāi)發(fā)的不朽經(jīng)典
生動(dòng)闡述重構(gòu)原理和具體做法
普通程序員進(jìn)階到編程高手必須修煉的秘笈

呃,再往下面離題越來(lái)越遠(yuǎn)了。
至此,關(guān)注點(diǎn)分離這塊內(nèi)容暫告一段落。

小春微信
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評(píng)論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,400評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,136評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,714評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,452評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,818評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,997評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,552評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,292評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,510評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,721評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,121評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,429評(píng)論 1 294
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,235評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,480評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容