面向?qū)ο笤O(shè)計的原則(OOD&OOP)主要分為兩大類歪架,一類是面向類的,另一類是面向包的夺鲜。設(shè)計模式基本都是圍繞面向類的幾個原則的實踐皆尔,而面向包的幾個原則主要體現(xiàn)在架構(gòu)模式中。
S.O.L.I.D
Bob大叔(Robert C. Martin)的大名如雷貫耳币励,相信大部分在進擊中的開發(fā)者都有閱讀過經(jīng)典著作《敏捷軟件開發(fā)》的經(jīng)歷慷蠕,受益匪淺。
在面向?qū)ο蟮某绦虻脑O(shè)計和開發(fā)過程中時食呻,有5個原則非常重要流炕,此外還有一個法則我們應(yīng)該盡量去遵守它。
1仅胞、單一職責(zé)原則(SRP每辟,The Single Responsibility Principle)
2、開放封閉原則(OCP干旧,The Open Closed Principle)
3渠欺、里氏替換原則(LSP,The Liskov Substitution Principle)
4椎眯、依賴倒置原則(DIP挠将,The Dependency Inversion Principle)
5、接口分離原則(ISP编整,The Interface Segregation Principle)
6舔稀、迪米特法則(LoD,The Law of Demeter闹击、LKP)
SRP
A class should have only one reason to change.
一個類應(yīng)該僅有一個引起它變化的原因镶蹋。
SRP是所有原則中最簡單的一個,概念簡潔易懂赏半。但卻也是最難正確運用贺归,或者說是最容易過度使用的原則。
一方面断箫,因為SRP把職責(zé)定義成了變化的原因拂酣,如果我們能夠找到多于一個的動機去改變一個類,那么這個類就具有多于一個的職責(zé)仲义。比如開發(fā)者經(jīng)常會習(xí)慣性的把職責(zé)歸類式的結(jié)合在一起婶熬,而正確的做法是發(fā)現(xiàn)職責(zé)并把這些職責(zé)相互分離。
另一方面埃撵,如果程序的變化方式總是導(dǎo)致N個職責(zé)(N>1)同時變化赵颅,那么就沒必要分離它們了。在這里有一個推論:僅當變化發(fā)生時暂刘,變化的軸線才具有實際的意義饺谬。在實際的設(shè)計和開發(fā)過程中,一定要把握好分離“單個”職責(zé)的度谣拣,所謂過猶不及募寨,過度的分離只能適得其反族展,違背了面向?qū)ο蟮姆庋b思想。
OCP
Software entities(classes,modules,functions,etc.) should be open for extension, but closed for modification.
軟件實體(如類拔鹰、模塊仪缸、函數(shù)等)應(yīng)該對擴展開放,對修改關(guān)閉列肢。
OCP是整個面向?qū)ο笤O(shè)計的核心恰画。它代表了靈活性、可重用性和可維護性例书。
乍一看锣尉,兩個特征似乎有所矛盾,擴展模塊的功能通常是以修改模塊的源代碼作為手段决采。但是,我們有“抽象”坟奥!通過創(chuàng)建穩(wěn)定的但又能描述一組任意個可能行為的抽象基類树瞭,并將具體的行為實現(xiàn)為派生類,這種做法能很好的解決開放擴展的問題爱谁。主要是針對軟件中頻繁變化的那部分作出合理的抽象晒喷。
當然,創(chuàng)建和使用抽象會增加軟件復(fù)雜性和團隊的精力访敌,所以肆意的進行抽象顯然并不是一個好設(shè)計凉敲,簡直是折磨人。除非你有強大的智商優(yōu)越感寺旺,但是面對廣大平凡質(zhì)樸的程序員爷抓,還請手下留情。
拒絕不成熟的抽象和抽象本身一樣重要阻塑。
LSP
Derived types must be completely substitutable for their base types.
子類型必須能夠完全的替換掉它們的基類型蓝撇。
LSP是使OCP成為可能的主要原則之一,也是繼承層次特征的主要設(shè)計原則陈莽。
一個軟件實體如果使用的是一個父類的話,那么一定適用于其子類,而且它察覺不出父類對象和子類對象的區(qū)別畏纲。也就是說筛武,在軟件中,把父類都替換成它的子類私植,程序行為沒有或者不發(fā)生變化忌栅。也就是說,只有當子類可以替換掉父類兵琳,軟件的功能不受到影響時狂秘,父類才能真正的被復(fù)用骇径,而子類也能夠真正的在父類的基礎(chǔ)上增加新的行為。
子類型的可替換性使得使用基類型表示的模塊在無需修改的情況下就可以擴展者春∑葡危可替換性可以通過顯示或者隱式的契約來定義。
DIP
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
高層模塊不應(yīng)該依賴于低層模塊钱烟,兩者都應(yīng)該依賴于抽象
B. Abstractions should not depend on details. Details should depend on abstractions.
抽象不應(yīng)該依賴于細節(jié)晰筛,細節(jié)應(yīng)該依賴于抽象
DIP是面向?qū)ο笤O(shè)計的標志。
依賴倒置原則是實現(xiàn)許多面向?qū)ο蠹夹g(shù)所宣稱的好處的基本低層機制拴袭,他的正確應(yīng)用對于創(chuàng)建可重用的框架來說是必須的读第。同時它對于構(gòu)建在變化面前富有彈性的代碼也是非常重要的,由于抽象和細節(jié)彼此隔離拥刻,所以代碼也非常容易維護怜瞒。
針對接口編程,而不要對實現(xiàn)編程般哼。倒置接口的所有權(quán)吴汪。
LSP和DIP乍一看還有點相似,其實蒸眠,他們還是有區(qū)別的:
兩個原則所站的角度不同漾橙。LSP是站在模式對象的角度,而DIP則是站在客戶端程序的角度楞卡;模式對象一方將“相對多變的”子類視同它的接口(或父類)霜运,而客戶程序依賴的內(nèi)容是“相對穩(wěn)定”的接口。
ISP
Clients should not be forced to depend upon interfaces that they don't use.
不應(yīng)該強迫客戶程序依賴并未使用的接口蒋腮。
ISP有效的降低了程序間的耦合性淘捡,提高了程序集的內(nèi)聚性。避免了接口污染徽惋。
客戶程序應(yīng)該僅僅依賴于他們實際調(diào)用的方法案淋。分離客戶就是分離接口。通常我們可以使用委托或者使用多重繼承來分離接口险绘。
接口隔離原則不是單純的多余或是不多余踢京,從設(shè)計的角度來衡量,它要依據(jù)業(yè)務(wù)領(lǐng)域的需要判斷倒是是否真的“多余”宦棺,確保在每個領(lǐng)域背景下貫徹不去依賴用不到的方法瓣距。通過把胖類的接口分解為多個特定于客戶程序的接口,可以實現(xiàn)這個目標代咸。有效的解除了客戶程序和它們沒有調(diào)用到的方法間的依賴關(guān)系蹈丸。
客戶程序看到的應(yīng)該是多個具有內(nèi)聚接口的抽象基類。
LoD(LKP)
A. Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
每一個軟件單位對其他的單位都只有最少的知識,而且局限于那些與本單位密切相關(guān)的軟件單位逻杖。
B. Each unit should only talk to its friends; don't talk to strangers.
C. Only talk to your immediate friends.
迪米特法則又稱為最少知識原則奋岁。主要有三層意思,就是讓調(diào)用者對于目標對象的知識最少荸百,而且只和你的直接朋友對話闻伶,千萬不要和陌生人說話。
該法則因在《程序員的修煉之道》一書中提及而聲名鵲起廣為人知够话。其初衷在于降低類之間的耦合蓝翰,但是由于通過友元類來建立一個類和其他類的聯(lián)系,或是減少對其他類的依賴女嘲,這導(dǎo)致了在一定程度上增加了系統(tǒng)的復(fù)雜度畜份。
小結(jié)
這些原則是數(shù)十年軟件工程經(jīng)驗來之不易的結(jié)果。是眾多開發(fā)人員和研究人員思想和著作的結(jié)晶欣尼。我們應(yīng)該牢記面向?qū)ο笤O(shè)計的原則爆雹,理解OO的設(shè)計思路。因為它們有助于開發(fā)人員消除拙劣的設(shè)計和代碼臭味愕鼓,并能有效的幫助開發(fā)人員構(gòu)建出最適合于當前特性集的設(shè)計顶别。
Bob大叔建議:在日常工作中,只有當出現(xiàn)“臭味”時我們才會去使用它拒啰,如果只是因為它是一個原則就無條件的遵循它那是錯誤的,過分的遵循這些原則會導(dǎo)致不必要的復(fù)雜性和設(shè)計臭味完慧。
原文