隨著軟件代碼規(guī)模的不斷擴(kuò)大也糊,代碼的維護(hù)成本越來越高,組件化勢在必行羡宙,設(shè)計(jì)組件時應(yīng)該考慮哪些問題狸剃?本文介紹了組件設(shè)計(jì)的六大原則。
隨著軟件代碼規(guī)模的不斷擴(kuò)大狗热,管理軟件的復(fù)雜性钞馁,使軟件容易擴(kuò)展,確保業(yè)務(wù)和研發(fā)效率的敏捷性越來越重要匿刮。項(xiàng)目架構(gòu)層面上僧凰,開閉原則告訴我們要將系統(tǒng)劃分為一系列組件,組件之間的依賴關(guān)系按照層次結(jié)構(gòu)進(jìn)行組織熟丸,從而使得系統(tǒng)容易擴(kuò)展训措。
大型軟件系統(tǒng)中,一般將系統(tǒng)分層光羞,比如基礎(chǔ)組件層绩鸣、業(yè)務(wù)邏輯層,數(shù)據(jù)持久層纱兑、服務(wù)層呀闻。橫向上,一般也會根據(jù)業(yè)務(wù)潜慎、功能進(jìn)行組件化总珠。在組件化過程中,哪些類應(yīng)該組合成一個組件勘纯?組件之間如何解耦?如何實(shí)現(xiàn)“高內(nèi)聚钓瞭,低耦合”驳遵?這些問題都是開發(fā)人員要謹(jǐn)慎考慮的問題。
SOLID設(shè)計(jì)原則主要關(guān)注類的設(shè)計(jì)山涡,解決如何將數(shù)據(jù)和函數(shù)設(shè)計(jì)成類堤结,以及如何將這些類鏈接起來成為程序的問題唆迁。
在Robert C. Martin的 《架構(gòu)整潔之道》 中,他提出了一些用于組件設(shè)計(jì)的原則竞穷,一共包括六個原則唐责。
組件聚合指導(dǎo)我們應(yīng)該將哪些類組合成一個組件,要考慮三個原則:復(fù)用/發(fā)布等同原則瘾带、共同閉包原則鼠哥、共同復(fù)用原則。
組件耦合幫助我們確定組件之間的相互依賴關(guān)系看政,要考慮三個原則:無依賴環(huán)原則朴恳、穩(wěn)定依賴原則、穩(wěn)定抽象原則允蚣。
組件聚合
組件聚合是應(yīng)該把哪些類應(yīng)該被組合成一個組件于颖?
復(fù)用/發(fā)布等同原則(The Reuse/Release Equivalence Principle, REP)
軟件復(fù)用的最小粒度等同于其發(fā)布的最小粒度。
REP告訴我們嚷兔,代碼復(fù)用在組件這一級森渐,可復(fù)用的組件中必須包含可復(fù)用的類,這些可復(fù)用的類以組件的方式發(fā)布給用戶使用冒晰。從另外一個角度去考慮一個組件的內(nèi)容同衣,一個組件中的類要么都可以重用,要么都不是可重用的翩剪。
REP要求我們保持組件的重用粒度和組件的發(fā)布粒度一致乳怎。例如,兩個組件A前弯、B蚪缀,如果其他組件總是一起使用組件A、B恕出,而且這兩個組件總是一起發(fā)布询枚,那么這兩個組件應(yīng)該合為一個組件。組件中的類與模塊必須是彼此緊密相關(guān)的
共同閉包原則(The Common Closure Principle, CCP)
應(yīng)該將那些會同時修改浙巫、相同目的修改的類放在同一個組件中金蜀。另一方面,應(yīng)該將不會同時修改的畴,不會為了相同的目的而修改的類放在不同的組件中渊抄。
CCP原則告訴我們,如果一個應(yīng)用中的代碼需要同時丧裁、為統(tǒng)一目的發(fā)生修改护桦,盡量讓這種修改都集中在一個組件中,而不是分散在多個組件中煎娇。如果將這些更改分散在多個組件中二庵,將增加軟件發(fā)布贪染、驗(yàn)證和維護(hù)工作量。
共同閉包原則不重視代碼的復(fù)用性催享,例如杭隙,如果A和B組件共同依賴于C組件,而且A組件的變化經(jīng)常伴隨著C變更因妙,按照CCP原則的要求痰憎,C組件應(yīng)該要放入A組件中,同樣C組件應(yīng)該放入B組件中兰迫,這將會導(dǎo)致代碼復(fù)用性降低信殊。
對于大部分應(yīng)用程序來說,可維護(hù)性的重要性要遠(yuǎn)遠(yuǎn)高于可復(fù)用性汁果!
共同復(fù)用原則(The Common Reuse Principle, CRP)
不要強(qiáng)迫一個組件依賴他們不需要的東西涡拘。
CRP原則是面向?qū)ο笤O(shè)計(jì)原則中接口分離原則的普適版本。CRP要求經(jīng)常共同復(fù)用的類和模塊放在同一個組件中据德,而不是緊密相連的類不應(yīng)該放在同一個組件中鳄乏。
一個組件中的類應(yīng)該一起被復(fù)用,相反棘利,如果你只用一個組件中的一部分類橱野,那么應(yīng)該把不用的類從組件中移除出去。
從另一個方面講善玫,在一個組件中不應(yīng)該包含太多不同類型的類水援,不要把一些完全不相干的類放在一個組件中,這樣會導(dǎo)致組件的職責(zé)過重茅郎,從而增加修改和發(fā)布的頻率蜗元。
哪個原則更重要?
你可能已經(jīng)意識到系冗,上述三個原則是相互競爭的奕扣。REP和CCP是粘合性原則,告訴我們哪些類要放在一起掌敬,這會讓組件變得更大惯豆。CRP是排除性的原則,不需要的類要從組件中移除出去奔害,這會使組件變小楷兽。軟件架構(gòu)師的重要任務(wù)就是在這三個原則之間做出均衡。
只關(guān)注兩個方面华临,而忽略另一個方面會引發(fā)一些問題:
- 只關(guān)注REP和CRP將會導(dǎo)致一個功能會拆分為更多組件芯杀,每次需求變更會導(dǎo)致太多的組件變更。
- 只關(guān)注REP和CCP將會導(dǎo)致每個組件規(guī)模很龐大,改動一個小點(diǎn)瘪匿,就會導(dǎo)致組件更新版本,從而使得組件頻繁發(fā)布寻馏。
- 只關(guān)注CCP和CRP將會導(dǎo)致不關(guān)注組件的復(fù)用性棋弥,使得組件復(fù)用困難。
如何進(jìn)行權(quán)衡诚欠?
Bob大叔建議我們顽染,要在上述三角區(qū)域中定位一個最適合目前研發(fā)團(tuán)隊(duì)狀態(tài)的位置,同時不停調(diào)整轰绵。
在項(xiàng)目早期粉寞,CCP原則會比REP原則更重要,因?yàn)檫@一階段開發(fā)速度要比復(fù)用性更重要左腔,這個時候可以犧牲復(fù)用性唧垦,換取開發(fā)速度。
隨著項(xiàng)目成熟液样,其他組件之間產(chǎn)生產(chǎn)生更多依賴振亮,項(xiàng)目重心就逐漸會向三角區(qū)域的左側(cè)滑動,更加重視復(fù)用性鞭莽。
也就是說坊秸,要根據(jù)項(xiàng)目的開發(fā)時間和成熟度不斷變動、不斷演化的澎怒,與項(xiàng)目本身的功能關(guān)系很小褒搔。
組件耦合
如何管理組件之間的依賴關(guān)系?
無依賴環(huán)原則 (Acyclic Dependencies Principle喷面,ADP)
組件依賴關(guān)系圖中不應(yīng)該出現(xiàn)環(huán)
環(huán)形依賴關(guān)系使得一個組件修改之后的影響范圍將變得非常大星瘾,環(huán)中任何一個組件發(fā)生變更,會對環(huán)上每一個組件產(chǎn)生影響乖酬,進(jìn)而導(dǎo)致對環(huán)上組件依賴的其他組件產(chǎn)生影響死相,導(dǎo)致系統(tǒng)難以升級和維護(hù)。
如何打破依賴循環(huán)咬像? 有兩種機(jī)制可以消除環(huán)形依賴關(guān)系:1)使用依賴反轉(zhuǎn)原則 2)創(chuàng)建一個新組件
下面舉例說明算撮,在這個例子中三個組件Interactors、Authorizer县昂、Entities三個組件形成環(huán)形依賴關(guān)系肮柜。
1)使用依賴反轉(zhuǎn)原則
步驟:
- 分析發(fā)現(xiàn),Entities組件中User使用到Authorizer組件中的認(rèn)證Permission功能
- 把User調(diào)用Authorizer組件中的認(rèn)證Permission抽象成為一個接口倒彰,User依賴這個接口审洞,而不是依賴于Authorizer
- Authorizer實(shí)現(xiàn)了Permission接口(是Permission的具體類),于是Authorizer依賴于Permission接口
- Authorizer依賴于Entities組件,實(shí)現(xiàn)了依賴反轉(zhuǎn)芒澜,打破了依賴循環(huán)仰剿。
2)創(chuàng)建新組件
1、把Entities組件中User使用到Authorizer組件中的認(rèn)證Permission功能單獨(dú)拆分出來痴晦,形成新的組件Permissions南吮。
2、Entities和Authorizer共同依賴于新的組件誊酌,打破了依賴循環(huán)部凑。
穩(wěn)定依賴原則(The Stable Dependencies Principle,SDP)
依賴關(guān)系必須指向更穩(wěn)定的方向
如何衡量一個組件的穩(wěn)定性碧浊?
不穩(wěn)定性指標(biāo):I = (對別的組件依賴個數(shù))/ (對別的組件依賴個數(shù) + 別的組件對自己依賴個數(shù))
每一個組件的I指標(biāo)必須大于其依賴組件的I指標(biāo)涂邀。
穩(wěn)定抽象原則(The Stable Abstractions Principle,SAP)
一個組件的抽象化程度應(yīng)該與其穩(wěn)定性保持一致箱锐。
組件的抽象化程度 A = 組件中抽象類和接口的數(shù)量/組件中具體類的數(shù)量比勉。
- 穩(wěn)定的組件應(yīng)該是抽象的,應(yīng)該由接口和抽象類組成瑞躺。
- 一個不穩(wěn)定的組件應(yīng)該包含具體的實(shí)現(xiàn)代碼敷搪。
- 依賴關(guān)系應(yīng)該指向更加抽象的方向。
總結(jié)
本文介紹了組件設(shè)計(jì)要考慮的原則幢哨,首先赡勘,要考慮應(yīng)該把哪些類應(yīng)該被組合成一個組件,組件太大和太小都會有不同的問題捞镰,組件聚合原則告訴我們設(shè)計(jì)組件時要考慮的原則闸与,以及如何根據(jù)項(xiàng)目的開發(fā)時間和成熟度對這些原則進(jìn)行權(quán)衡。組件耦合考慮的是如何管理組件之間的依賴關(guān)系岸售,減小組件之間的耦合践樱,組件依賴要考慮的問題。從組件聚合和組件耦合全面分析凸丸,可以設(shè)計(jì)出"高內(nèi)聚性锭、低耦合"的組件涉馁。