很想寫相關的內(nèi)容降狠,一直以來這方面的東西很雜对竣,自己各方面都多多少少有些總結(jié),但是沒有系統(tǒng)的成文喊熟,始終覺得是個遺憾柏肪。
這是這個系列的第一篇。
本文說的架構(gòu)芥牌,還并不是說的Tier層的架構(gòu)烦味,這里面不會涉及到分布式、緩存壁拉、網(wǎng)絡結(jié)構(gòu)等等的布局谬俄,而是集中在軟件的內(nèi)部,是代碼層級的弃理,考慮這點架構(gòu)的點溃论,目的是在于幫助我們寫出清晰、易維護的軟件痘昌。
關注點分離(Separation of concerns, SoC)
這個準則應該作為我們開發(fā)和架構(gòu)的指導性的原則钥勋。在該原則下,軟件應該按照其業(yè)務來將軟件本身劃分成不同的部分辆苔,從而進一步降低耦合性算灸,不過,這感覺是句廢話驻啤,大家好像都懂菲驴。
那么首先,關注點是什么呢骑冗?
比如說一組對代碼有影響的業(yè)務邏輯赊瞬,或?qū)δ硞€具體業(yè)務有影響的業(yè)務規(guī)則。它其實可以很通用贼涩,比如針對x86環(huán)境優(yōu)化代碼的細節(jié)巧涧;也可以很具體,比如某個將要初始化的類的名字遥倦,只要它對我們是有用的褒侧,我們就稱它為其中的一個關注點。
舉例來說谊迄,如果某個軟件有個邏輯:是將某些產(chǎn)品高亮顯示出來闷供,以顯示這些產(chǎn)品的獨特性。
那么统诺,把這些產(chǎn)品挑選出來的邏輯歪脏,應該和把這些產(chǎn)品做高亮的邏輯分離開來,這是兩個不同的關注點(只是剛好這兩個關注點是互相關聯(lián)的而已)粮呢。
在架構(gòu)上婿失,如何去應用這條準則呢钞艇?比如說,把業(yè)務邏輯的行為分成基本的實現(xiàn)層(infrastruture)和UI層(理想的情況下豪硅,業(yè)務規(guī)則和業(yè)務邏輯都應該分離到不同的項目里面去哩照,他們也不能互相產(chǎn)生依賴的關系)。這種結(jié)構(gòu)能幫助我們保證業(yè)務邏輯更容易的測試和應用懒浮,而且在底層也沒有互相耦合在一起飘弧。
關注點分離是我們對于軟件分層的一個核心的考慮點。
把握好這個尺度砚著,有助于我們建造模塊化的應用程序次伶。它的價值在于簡化開發(fā)和提高維護性。這個準則做好了稽穆,各獨立部分就能重用冠王,也可以相對獨立的開發(fā)和更新,某個模塊更新了舌镶,其他的模塊不必做額外的修改柱彻。
但是,聽起來還是好抽象的感覺餐胀。
舉例來說哟楷,ASP.NET MVC就是關注點分離的一個體現(xiàn),它將原來的ASP.NET WebForm分離成模型(model)-視圖(view)-控制器(controller)骂澄,從而把業(yè)務邏輯吓蘑、數(shù)據(jù)惕虑、界面分離坟冲,這也是組織代碼結(jié)構(gòu)的一個形式。
MVC的基本結(jié)構(gòu):
Model層表示應用程序的數(shù)據(jù)核心溃蔫,通常負責在數(shù)據(jù)庫中存取數(shù)據(jù)健提。
View是應用程序的顯示層,通常是依據(jù)模型的數(shù)據(jù)而建立伟叛。
-
Controller是用來控制和處理輸入輸出的私痹,是處理用戶交互的部分,也負責向模型(Model層)發(fā)送數(shù)據(jù)统刮。
MVC的這個設計各個關注點是分開的紊遵,這樣有助于我們管理和開發(fā)復雜的應用程序,我們可以在某個時間點只集中精力在其中的某一個關注點侥蒙,而不是所有的部分暗膜。舉例來說,前端的開發(fā)人員可以配合設計團隊繞過業(yè)務邏輯鞭衩,專注在視圖和交互設計部分学搜。另外的一端娃善,DBA也可以配合某個團隊專注在數(shù)據(jù)持久化的部分,而中間的業(yè)務邏輯層又可以由其他團隊集中精力來負責瑞佩。這種分層也簡化了分組開發(fā)聚磺,讓測試也更為容易。
除了ASP.NET MVC還有其他的框架也是這樣的關注點分離的思想炬丸,比如Django瘫寝,Structs,Spring等等御雕。
那么分層思想都有哪些方法呢矢沿,總不至于只是用個MVC就結(jié)束了吧?
其實東西還是比較多的酸纲,下面捣鲸,將介紹一些分層的思想。
縱向分離
大家都懂闽坡,即便是最初級的程序員也都接觸過栽惶,可能是你并沒有意識到而已。
我們十好幾年前的三層架構(gòu)疾嗅,界面層(UI Layer)外厂,業(yè)務邏輯層(Business Layer)和數(shù)據(jù)持久化層(Data Access Layer),就是這一種自上而下的縱向的分層手法代承。
橫向分離
大家也懂汁蝶。我們倡導的模塊化的編程,把我們的軟件拆分成模塊或子系統(tǒng)论悴。
從左到右是模塊1掖棉、模塊2、模塊3膀估,這是一種水平方向的切割幔亥。
這跟縱向的分離是兩個不同的方向,橫向分離大多是模塊化的過程察纯。
切面分離
有些內(nèi)容是多個層之間都需要的帕棉,比如日志(logging),在你的系統(tǒng)里面饼记,界面層香伴、邏輯層、數(shù)據(jù)訪問層可能都需要寫日志具则,這種跨到多層同樣邏輯就可以考慮切面分離即纲。
在asp.net mvc中,我們可以使用filter來實現(xiàn), Spring中也有SpringAOP等等乡洼。
依賴方向分離
我們考慮這幾點:
- 有些類要修改的幾率比其他的類修改的幾率大得多崇裁。
- 具體的類比抽象類修改的幾率大得多匕坯。
- 修改被依賴得很多的類可能引起很大的改動。
- 某些類比其他類被重用的可能性大得多拔稳。
依據(jù)這些考慮點葛峻,我們來決定某個類應該放在哪個層次里面,或者考慮將某一層切割成多層巴比。
關注數(shù)據(jù)分離
在組織數(shù)據(jù)時术奖,應該盡量考慮數(shù)據(jù)本身的固有屬性,如果不是它們的固有屬性轻绞,那么應該分離出來采记。
比如產(chǎn)品的類就不應該關聯(lián)customer類,因為產(chǎn)品不應該跟客戶直接產(chǎn)生數(shù)據(jù)關系政勃,產(chǎn)品的顏色唧龄、型號、描述才是產(chǎn)品該有的固有屬性奸远。
至于客戶既棺,應該是用訂單類來把他們聯(lián)系在一起。
關注行為分離
跟上面講的一樣懒叛,行為也應該是事物或?qū)ο蟮墓逃械谋旧淼男袨橥杳幔黠@偏離原來行為的,應該考慮成另外的關注點兒分離開薛窥。
比如有一個函數(shù)叫做CreateNewCustomer()胖烛,那么CreateNewCustomer的行為就應該限定在創(chuàng)建一個新客戶上面,給新客戶自動發(fā)優(yōu)惠券的動作就不能放到這個函數(shù)里面诅迷。
擴展分離
如果基于某種設計佩番,原先不具有某些行為需要增加,可以考慮通過擴展或插件的形式來完成竟贯,將這些功能放入到插件或擴展中答捕,就是擴展分離逝钥。
比如Firefox屑那、Chrome的去廣告的插件,這些功能增加了系統(tǒng)原本的行為艘款,將這些行為分離到插件里面去持际,就是擴展分離。
委托分離
如果某個行為還無法具體確定哗咆,可以使用委托的方式蜘欲。
比如C#的delegate,當我們還不知道某些具體行為應該如何實現(xiàn)晌柬,或者不應該在此處對該行為進行實現(xiàn)姥份,或者有多個行為可以互相替代郭脂,就可以將函數(shù)的參數(shù)指定為一個delegate。
至于delegate具體怎樣實現(xiàn)澈歉,那是其他部分應該關注的點展鸡。
比如現(xiàn)在需要將Customer的信息持久化,就可以把這個請求委托給DatabaseManager或WebSerivceManager埃难,由他們自行處理數(shù)據(jù)莹弊,然后返回給我結(jié)果。
反轉(zhuǎn)分離
現(xiàn)在有了很多的依賴注入的框架涡尘,像Autofac忍弛,Unit,Castle Windsor等等考抄,這些幫助我們做依賴翻轉(zhuǎn)细疚,從而倒置依賴關系。
要指出是川梅,上面提到了9種分離層次的概念惠昔,每一種概念都可以任意的與其他概念組合在一起,從而產(chǎn)生更多的變化挑势。
在實際的開發(fā)過程中镇防,沒有東西是一成不變的,而層次和架構(gòu)也應該是在開發(fā)的過程里面不斷完善和重構(gòu)潮饱。
初級程序員最煩的是需求或業(yè)務的修改来氧,一些我們覺得奇奇怪怪的修改導致大家不斷的修改代碼,心里很煩香拉,在心里也默默的把產(chǎn)品經(jīng)理被翻過來倒過去罵了千百遍啦扬。
但是,在實際的工作中你會發(fā)現(xiàn)凫碌,軟件開發(fā)就是這樣扑毡,沒有什么是不變的。
如果一定要找出一個不變的點盛险,我想那應該是:
唯一不變的瞄摊,就是變化。
關于不斷的修改與重構(gòu)苦掘,也可以參考《重構(gòu)-改善既有代碼的設計》换帜,記得封面上寫的是:
軟件開發(fā)的不朽經(jīng)典
生動闡述重構(gòu)原理和具體做法
普通程序員進階到編程高手必須修煉的秘笈
呃,再往下面離題越來越遠了鹤啡。
至此惯驼,關注點分離這塊內(nèi)容暫告一段落。