软件设计原则
1. 面向?qū)ο笤瓌t
(1)開閉原則
所謂開閉原則(Open Closed Principle, OCP)指的就是“軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉”,簡單講就是軟件系統(tǒng)中包含的各種組件應(yīng)該在不修改現(xiàn)有代碼的基礎(chǔ)上,引入新功能。開閉原則中“開”,是指對(duì)于組件功能的擴(kuò)展是開放的,是允許對(duì)其進(jìn)行功能擴(kuò)展的;開閉原則中“閉”,是指對(duì)于原有代碼的修改是封閉的,即不應(yīng)該修改原有的代碼。
(2)里氏替換原則
里氏替換原則(Liskov Substitution Principle,LSP)中說,任何基類可以出現(xiàn)的地方,子類一定可以出現(xiàn)。里氏替換原則可以理解為是對(duì)開閉原則的補(bǔ)充。實(shí)現(xiàn)開閉原則的關(guān)鍵步驟就是抽象化。而基類與子類的繼承關(guān)系就是抽象化的具體實(shí)現(xiàn),所以里氏替換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。
(3)依賴倒置原則
依賴倒置原則(Dependence Inversion Principle,DIP)就是說要依賴于抽象,不要依賴于實(shí)現(xiàn)。也即意味著要針對(duì)接口編程,不要針對(duì)實(shí)現(xiàn)編程,具體表現(xiàn)上在應(yīng)當(dāng)使用接口和抽象類進(jìn)行各種類型聲明以及數(shù)據(jù)類型的轉(zhuǎn)換。可以認(rèn)為開閉原則與依賴倒置原則是目標(biāo)和手段的關(guān)系。如果說開閉原則是目標(biāo),依賴倒轉(zhuǎn)原則是到達(dá)開閉原則的手段。如果要達(dá)到最好的開閉原則,就要盡量的遵守依賴倒置原則。
(4)單一職責(zé)原則
單一職責(zé)原則(Simple Responsibility Pinciple,SRP)強(qiáng)調(diào)一個(gè)類應(yīng)該只有一個(gè)職責(zé),并把職責(zé)定義為變化的原因。每一個(gè)職責(zé)都是變化的一個(gè)維度,如果一個(gè)類有一個(gè)以上的職責(zé),這些職責(zé)就耦合在了一起,當(dāng)一個(gè)職責(zé)發(fā)生變化時(shí),可能會(huì)影響其它的職責(zé),這就會(huì)導(dǎo)致脆弱的設(shè)計(jì)。另外,多個(gè)職責(zé)耦合在一起也會(huì)影響復(fù)用性。如果發(fā)現(xiàn)一個(gè)類有多于一個(gè)的職責(zé),就應(yīng)該通過分離接口等方式做到盡量解耦。
(5)接口隔離原則
關(guān)于隔離的含義就在于類之間的依賴關(guān)系應(yīng)該建立在最小的接口上,一個(gè)類不應(yīng)該依賴它不需用的接口。接口隔離原則(Interface Segregation Principle,ISP)就是說建立單一接口,不要建立臃腫龐大的接口。接口隔離原則與單一職責(zé)的審視角度是不相同的,單一職責(zé)要求的是類和接口職責(zé)單一,注重的是職責(zé),這是業(yè)務(wù)邏輯上的劃分,而接口隔離原則要求接口盡量細(xì)化,接口的方法盡量少
以下五條原則一般被稱為面向?qū)ο箢I(lǐng)域的S(SRP).O(OCP).L(LSP).I(ISP).D(DIP)原則。
2. 組件設(shè)計(jì)原則
組件設(shè)計(jì)原則有時(shí)候也稱為分包(Package)原則。任何一個(gè)軟件系統(tǒng)都可以看做是一系列組件的集合,良好的組件設(shè)計(jì)能夠把系統(tǒng)分解為一些大小恰到好處的組件,從而使每個(gè)開發(fā)團(tuán)隊(duì)都可以只關(guān)注單個(gè)的組件而無需關(guān)心整個(gè)系統(tǒng)。對(duì)于組件而言,最核心的關(guān)注點(diǎn)就是內(nèi)聚(Cohesion)和耦合(Coupling),所謂內(nèi)聚是指一個(gè)模塊內(nèi)各個(gè)元素彼此結(jié)合的緊密程度,而耦合指的是一個(gè)軟件結(jié)構(gòu)內(nèi)不同模塊之間互連程度的度量。基于這兩個(gè)關(guān)注點(diǎn),組件設(shè)計(jì)原則也包括組件內(nèi)聚原則(Component Cohesion Principle)和組件耦合原則(Component Coupling Principle)兩大類。
組件內(nèi)聚原則包括:
(1)重用-發(fā)布等價(jià)原則
重用-發(fā)布等價(jià)原則(Release-Reuse Equivalency Principle,REP)關(guān)注粒度,強(qiáng)調(diào)重用的粒度等于發(fā)布的粒度。重用-發(fā)布等價(jià)思想從用戶觀點(diǎn)的角度上為我們規(guī)范了組件設(shè)計(jì)的原則:在設(shè)計(jì)組件時(shí),組件中應(yīng)該包含的元素要么都可以重用,要么都不可以重用。
(2)共同封閉原則
共同封閉原則(Common Closure Principle,CCP)關(guān)注變化,即一個(gè)組件不應(yīng)該包括多個(gè)引起變化的原因。組件中所有類對(duì)同一種性質(zhì)的變化是共同封閉的,一個(gè)變化如果對(duì)一個(gè)封閉的組件產(chǎn)生影響,則將對(duì)該組件中的所有類產(chǎn)生影響,但對(duì)其他組件將不產(chǎn)生影響。該原則類似開放封閉原則,即對(duì)修改應(yīng)該是封閉的,但對(duì)擴(kuò)展應(yīng)該是開放的。從這個(gè)角度看,組件越大越能滿足共同封閉原則。
(3)共同重用原則
共同重用原則(Common Reuse Principle,CRP)關(guān)注重用,認(rèn)為一個(gè)組件中的所有類應(yīng)該是共同重用的,如果重用了組件中的一個(gè)類就應(yīng)該重用組件中的所有類。即放入一個(gè)組件中的類是不可分開的,僅僅依賴其中一部分類的情況不應(yīng)該存在。顯然,根據(jù)共同重用原則,組件應(yīng)該越小越好。
我們可以明顯看到共同封閉原則和共同重用原則具有互斥性,不同的原則面向不同的場景和生命周期。同樣,組件耦合原則也包含以下三條設(shè)計(jì)原則:
(4)無環(huán)依賴原則
無環(huán)依賴原則(Acyclic Dependencies Principle,ADP)認(rèn)為在組件之間不應(yīng)該存在循環(huán)依賴關(guān)系。通過將系統(tǒng)劃分為不同的可發(fā)布組件,對(duì)某一個(gè)組件的修改所產(chǎn)生的影響不應(yīng)該必須擴(kuò)展到其他組件。我們在后續(xù)的4.4.1節(jié)架構(gòu)模式中會(huì)進(jìn)一步探討當(dāng)組件之間存在循環(huán)依賴時(shí)如何進(jìn)行分析和消除。
(5)穩(wěn)定抽象原則
穩(wěn)定抽象原則(Stable Abstractions Principle,SAP)認(rèn)為組件的抽象程度應(yīng)該與其穩(wěn)定程度保持一致。即一個(gè)穩(wěn)定的組件應(yīng)該也是抽象的,這樣該組件的穩(wěn)定性就不會(huì)無法擴(kuò)展。另一方面,一個(gè)不穩(wěn)定的組件應(yīng)該是具體的,因?yàn)樗牟环€(wěn)定性使其內(nèi)部代碼更易于修改。
(6)穩(wěn)定依賴原則
穩(wěn)定依賴原則(Stable Dependencies Principle,SDP)認(rèn)為被依賴者應(yīng)該比依賴者更穩(wěn)定。一個(gè)好的設(shè)計(jì)中的組件之間的依賴應(yīng)該朝著穩(wěn)定的方向進(jìn)行。一個(gè)組件只應(yīng)該依賴那些比自己更穩(wěn)定的組件。在下圖中,我們認(rèn)為組件X是穩(wěn)定的,因?yàn)閄被很多其他組件依賴,相當(dāng)于責(zé)任擔(dān)當(dāng)著。而X沒有依賴別的包,所有它具備很高的獨(dú)立性。同時(shí),我們認(rèn)為組件Y是不穩(wěn)定的,因?yàn)閅沒有被其他的組件所依賴,但Y自身依賴很多別的組件。
?
3. 其他原則
除了面向?qū)ο蠛徒M件設(shè)計(jì)原則,業(yè)界還存在一批具有代表性的設(shè)計(jì)原則,包括:
(1)合成/聚合復(fù)用原則
合成/聚合復(fù)用原則(Composite/Aggregate Reuse Principle,CARP)強(qiáng)調(diào)在一個(gè)新的對(duì)象里面使用一些已有的對(duì)象,使之成為新對(duì)象的一部分,而新的對(duì)象通過向這些對(duì)象的委派達(dá)到復(fù)用這些對(duì)象的目的。也即推薦首先使用合成/聚合,合成/聚合則使系統(tǒng)靈活,其次才考慮繼承,達(dá)到復(fù)用的目的。而使用繼承時(shí),要嚴(yán)格遵循里氏替換原則。有效地使用繼承會(huì)有助于對(duì)問題的理解,降低復(fù)雜度,而濫用繼承會(huì)增加系統(tǒng)構(gòu)建、維護(hù)時(shí)的難度及系統(tǒng)的復(fù)雜度。所以合成/聚合復(fù)用原則在很多時(shí)候就體現(xiàn)為一句話,即組合由于繼承。
(2)迪米特法則
迪米特法則(Law of Demeter,LoD)認(rèn)為一個(gè)軟件實(shí)體應(yīng)當(dāng)盡可能少的與其他實(shí)體發(fā)生相互作用。這樣,當(dāng)一個(gè)模塊修改時(shí),就會(huì)盡量少的影響其他的模塊,擴(kuò)展也會(huì)相對(duì)容易。這是對(duì)軟件實(shí)體之間通信的限制,它要求限制軟件實(shí)體之間通信的寬度和深度。如果兩個(gè)類不必彼此直接通信,那么這兩個(gè)類就不應(yīng)當(dāng)發(fā)生直接的相互作用。如果其中的一個(gè)類需要調(diào)用另一個(gè)類的某一個(gè)方法的話,可以通過第三者轉(zhuǎn)發(fā)這個(gè)調(diào)用。設(shè)計(jì)模式中的門面模式和調(diào)停者模式實(shí)際上就是迪米特法則的具體應(yīng)用。
(3)命令-查詢分離原則
當(dāng)一個(gè)方法返回一個(gè)值來響應(yīng)一個(gè)請(qǐng)求,它就具有查詢(Query)的性質(zhì);當(dāng)一個(gè)方法要改變對(duì)象的狀態(tài),它就具有命令(Command)的性質(zhì)。通常,一個(gè)方法可能是純的Command模式或者是純的Query模式,或者是兩者的混合體。在設(shè)計(jì)接口時(shí),如果可能,應(yīng)該盡量使接口單一化,保證方法的行為嚴(yán)格的是命令或者是查詢,這樣查詢方法不會(huì)改變對(duì)象的狀態(tài),沒有副作用,而會(huì)改變對(duì)象的狀態(tài)的方法不可能有返回值。這就是命令-查詢分離(Command-Query Separation,CQS)原則。查詢功能和命令功能的分離,有助于系統(tǒng)性能,也有利于系統(tǒng)的安全性。
(4)慣例優(yōu)于配置原則
慣例優(yōu)于配置(Convention over Configuration,CoC)原則簡單講就是將一些公認(rèn)的配置方式和信息作為內(nèi)部缺省的規(guī)則來使用。我們的應(yīng)用只需要指定慣例外的信息即可,從而減少了大量配置信息,在大型系統(tǒng)中,過多的配置信息很多時(shí)候已經(jīng)成為影響開發(fā)效率的一個(gè)源頭。目前流行的微服務(wù)開發(fā)框架SpringBoot就是該原則的典型表現(xiàn)。
(5)關(guān)注點(diǎn)分離原則
關(guān)注點(diǎn)分離(Separation of Concerns,SoC)原則就是指在軟件開發(fā)中,通過各種手段將問題的各個(gè)關(guān)注點(diǎn)分開。實(shí)現(xiàn)關(guān)注點(diǎn)分離的方法主要有兩種,一種是標(biāo)準(zhǔn)化,另一種是抽象與包裝。標(biāo)準(zhǔn)化就是制定一套標(biāo)準(zhǔn),讓使用者都遵守它,這樣使用標(biāo)準(zhǔn)的人就不用擔(dān)心別人會(huì)有很多種不同的實(shí)現(xiàn),使自己的程序不能和別人的兼容。另一方面,不斷地把程序的某些部分抽象并包裝起來,也是實(shí)現(xiàn)關(guān)注點(diǎn)分離的好方法。諸如組件、分層、面向服務(wù)等概念都是在不同的層次上做抽象和包裝以使得使用者不用關(guān)心它的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)。
?
如果對(duì)文章感興趣,可以關(guān)注我的微信公眾號(hào):程序員向架構(gòu)師轉(zhuǎn)型,或掃描下面的二維碼。
我出版了《系統(tǒng)架構(gòu)設(shè)計(jì):程序員向架構(gòu)師轉(zhuǎn)型之路》、《向技術(shù)管理者轉(zhuǎn)型:軟件開發(fā)人員跨越行業(yè)、技術(shù)、管理的轉(zhuǎn)型思維與實(shí)踐》、《微服務(wù)設(shè)計(jì)原理與架構(gòu)》、《微服務(wù)架構(gòu)實(shí)戰(zhàn)》等書籍,并翻譯有《深入RabbitMQ》和《Spring5響應(yīng)式編程實(shí)戰(zhàn)》,歡迎交流。
總結(jié)
- 上一篇: densepose的IUV图像I通道数字
- 下一篇: WinForm与脚本的交互