正如牛頓三大定律在經典力學中的位置同樣,「開-閉」原則(Open-Closed Principle)是面向對象的可複用設計(Object Oriented Design或OOD)的基石。其餘設計原則(里氏代換原則、依賴倒轉原則、合成/聚合複用原則、迪米特法則、接口隔離原則)是實現「開-閉」原則的手段和工具。html
一.開-閉原則(Open-Closed Principle, OCP)程序員
1.什麼是開閉原則算法
1988年,Bertrand Meyer在他的著做《Object Oriented Software Construction》中提出了開閉原則,它的原文是這樣:「Software entities should be open for extension,but closed for modification」。翻譯過來就是:「軟件實體應當對擴展開放,對修改關閉」。這句話說得略微有點專業,咱們把它講得更通俗一點,也就是:軟件系統中包含的各類組件,例如模塊(Modules)、類(Classes)以及功能(Functions)等等,應該在不修改現有代碼的基礎上,引入新功能。開閉原則中「開」,是指對於組件功能的擴展是開放的,是容許對其進行功能擴展的;開閉原則中「閉」,是指對於原有代碼的修改是封閉的,即不該該修改原有的代碼。編程
2.如何實現開閉原則c#
實現開閉原則的關鍵就在於「抽象」。把系統的全部可能的行爲抽象成一個抽象底層,這個抽象底層規定出全部的具體實現必須提供的方法的特徵。做爲系統設計的抽象層,要預見全部可能的擴展,從而使得在任何擴展狀況下,系統的抽象底層不需修改;同時,因爲能夠從抽象底層導出一個或多個新的具體實現,能夠改變系統的行爲,所以系統設計對擴展是開放的。設計模式
咱們在軟件開發的過程當中,一直都是提倡需求導向的。這就要求咱們在設計的時候,要很是清楚地瞭解用戶需求,判斷需求中包含的可能的變化,從而明確在什麼狀況下使用開閉原則。ide
關於系統可變的部分,還有一個更具體的原則是對可變性的封裝原則(Principle of Encapsulation of Variation, EVP),它從軟件工程實現的角度對開閉原則進行了進一步的解釋。EVP要求在作系統設計的時候,對系統全部可能發生變化的部分進行評估和分類,每個可變的因素都單獨進行封裝。「對可變性的封裝原則」意味着兩點:函數
(1)一種可變性不該當散落在代碼的不少角落裏,而應當被封裝到一個對象裏面。同一種可變性的不一樣表象意味着同一個繼承等級結構中的具體子類,所以,咱們能夠期待在設計模式中看到繼承關係。繼承應當被看作是封裝變化的方法,而不該當被認爲是從通常的對象生成特殊的對象的方法。工具
(2)一種可變性不該當與另外一種可變性混合在一塊兒。測試
咱們在實際開發過程的設計開始階段,就要羅列出來系統全部可能的行爲,並把這些行爲加入到抽象底層,根本就是不可能的,這麼去作也是不經濟的,費時費力。另外,在設計開始階段,對全部的可變因素進行預計和封裝也不太現實,也是很難作獲得。因此,開閉原則描繪的願景只是一種理想狀況或是極端狀態,現實世界中是很難被徹底實現的。咱們只能在某些組件,在某種程度上符合開閉原則的要求。
經過以上的分析,對於開閉原則,咱們能夠得出這樣的結論:雖然咱們不可能作到百分之百的封閉,可是在系統設計的時候,咱們仍是要儘可能作到這一點。
對於軟件系統的功能擴展,咱們能夠經過繼承、重載或者委託等手段實現。以接口爲例,它對修改就是是封閉的,而對具體的實現是開放的,咱們能夠根據實際的須要提供不一樣的實現,因此接口是符合開閉原則的。
3.開閉原則可以帶來什麼好處
若是一個軟件系統符合開閉原則的,那麼從軟件工程的角度來看,它至少具備這樣的好處:
(1)可複用性好
咱們能夠在軟件完成之後,仍然能夠對軟件進行擴展,加入新的功能,很是靈活。所以,這個軟件系統就能夠經過不斷地增長新的組件,來知足不斷變化的需求。
(2)可維護性好
因爲對於已有的軟件系統的組件,特別是它的抽象底層不去修改,所以,咱們不用擔憂軟件系統中原有組件的穩定性,這就使變化中的軟件系統有必定的穩定性和延續性。
4.閉原則與其它原則的關係
開閉原則具備理想主義的色彩,它是面向對象設計的終極目標。所以,針對開閉原則的實現方法,一直都有面向對象設計的大師費盡心機,研究開閉原則的實現方式。其它原則和方法如:里氏代換原則(LSP)、依賴倒轉原則(DIP)、接口隔離原則(ISP)以及抽象類(Abstract Class)、接口(Interace)等等,均可以看做是開閉原則的實現方法。
此部分參考:http://www.cnblogs.com/wanghao72214/archive/2009/03/13/1410610.html
二.里氏代換原則(Liskov Substitution Principle,LSP)
里氏代換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 里氏代換原則中說,任何基類能夠出現的地方,子類必定能夠出現。 LSP是繼承複用的基石,只有當衍生類能夠替換掉基類,軟件單位的功能不受到影響時,基類才能真正被複用,而衍生類也可以在基類的基礎上增長新的行爲。里氏代換原則是對「開-閉」原則的補充。實現「開-閉」原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,因此里氏代換原則是對實現抽象化的具體步驟的規範。
LSP講的是基類和子類的關係。只有當這種關係存在時,里氏代換關係才存在。若是兩個具體的類A,B之間的關係違反了LSP的設計,(假設是從B到A的繼承關係)那麼根據具體的狀況能夠在下面的兩種重構方案中選擇一種。
-----建立一個新的抽象類C,做爲兩個具體類的超類,將A,B的共同行爲移動到C中來解決問題。
-----從B到A的繼承關係改成委派關係。
爲了說明,咱們先用第一種方法來看一個例子,第二種辦法在另一個原則中說明。咱們就看那個著名的長方形和正方形的例子。對於長方形的類,若是它的長寬相等,那麼它就是一個正方形,所以,長方形類的對象中有一些正方形的對象。對於一個正方形的類,它的方法有個setSide和getSide,它不是長方形的子類,和長方形也不會符合LSP。
eg:
長方形類:
public class Rectangle{
...
setWidth(int width){
this.width=width;
}
setHeight(int height){
this.height=height
}
}
正方形類:
public class Square{
...
setWidth(int width){
this.width=width;
this. height=width;
}
setHeight(int height){
this.setWidth(height);
}
}
例子中改變邊長的函數:
public void resize(Rectangle r){
while(r.getHeight()<r.getWidth){
r.setHeight(r.getWidth+1);
}
}
那麼,若是讓正方形當作是長方形的子類,會出現什麼狀況呢?咱們讓正方形從長方形繼承,而後在它的內部設置width等於height,這樣,只要width或者height被賦值,那麼width和height會被同時賦值,這樣就保證了正方形類中,width和height老是相等的.如今咱們假設有個客戶類,其中有個方法,規則是這樣的,測試傳入的長方形的寬度是否大於高度,若是知足就中止下來,不然就增長寬度的值。如今咱們來看,若是傳入的是基類長方形,這個運行的很好。根據LSP,咱們把基類替換成它的子類,結果應該也是同樣的,可是由於正方形類的width和height會同時賦值,這個方法沒有結束的時候,條件老是不知足,也就是說,替換成子類後,程序的行爲發生了變化,它不知足LSP。
那麼咱們用第一種方案進行重構,咱們構造一個抽象的四邊形類,把長方形和正方形共同的行爲放到這個四邊形類裏面,讓長方形和正方形都是它的子類,問題就OK了。對於長方形和正方形,取width和height是它們共同的行爲,可是給width和height賦值,二者行爲不一樣,所以,這個抽象的四邊形的類只有取值方法,沒有賦值方法。上面的例子中那個方法只會適用於不一樣的子類,LSP也就不會被破壞。
在進行設計的時候,咱們儘可能從抽象類繼承,而不是從具體類繼承。若是從繼承等級樹來看,全部葉子節點應當是具體類,而全部的樹枝節點應當是抽象類或者接口。固然這個只是一個通常性的指導原則,使用的時候還要具體狀況具體分析。
三.依賴倒置原則(Dependence Inversion Principle,DIP)
1.概念
依賴倒轉原則就是要依賴於抽象,不要依賴於實現。(Abstractions should not depend upon details. Details should depend upon abstractions.)要針對接口編程,不要針對實現編程。(Program to an interface, not an implementation.)也就是說應當使用接口和抽象類進行變量類型聲明、參數類型聲明、方法返還類型說明,以及數據類型的轉換等。而不要用具體類進行變量的類型聲明、參數類型聲明、方法返還類型說明,以及數據類型的轉換等。要保證作到這一點,一個具體類應當只實現接口和抽象類中聲明過的方法,而不要給出多餘的方法。
傳統的過程性系統的設計辦法傾向於使高層次的模塊依賴於低層次的模塊,抽象層次依賴於具體層次。倒轉原則就是把這個錯誤的依賴關係倒轉過來。面向對象設計的重要原則是建立抽象化,而且從抽象化導出具體化,具體化給出不一樣的實現。繼承關係就是一種從抽象化到具體化的導出。抽象層包含的應該是應用系統的商務邏輯和宏觀的、對整個系統來講重要的戰略性決定,是必然性的體現。具體層次含有的是一些次要的與實現有關的算法和邏輯,以及戰術性的決定,帶有至關大的偶然性選擇。具體層次的代碼是常常變更的,不能避免出現錯誤。
從複用的角度來講,高層次的模塊是應當複用的,並且是複用的重點,由於它含有一個應用系統最重要的宏觀商務邏輯,是較爲穩定的。而在傳統的過程性設計中,複用則側重於具體層次模塊的複用。依賴倒轉原則則是對傳統的過程性設計方法的「倒轉」,是高層次模塊複用及其可維護性的有效規範。
特例:對象的建立過程是違背「開—閉」原則以及依賴倒轉原則的,但經過工廠模式,能很好地解決對象建立過程當中的依賴倒轉問題。
2.關係
「開-閉」原則與依賴倒轉原則是目標和手段的關係。若是說開閉原則是目標,依賴倒轉原則是到達"開閉"原則的手段。若是要達到最好的"開閉"原則,就要儘可能的遵照依賴倒轉原則,依賴倒轉原則是對"抽象化"的最好規範。
里氏代換原則是依賴倒轉原則的基礎,依賴倒轉原則是里氏代換原則的重要補充。
3.耦合(依賴)關係的種類
零耦合(Nil Coupling)關係:兩個類沒有耦合關係
具體耦合(Concrete Coupling)關係:發生在兩個具體的(可實例化的)類之間,經由一個類對另外一個具體類的直接引用形成。
抽象耦合(Abstract Coupling)關係:發生在一個具體類和一個抽象類(或接口)之間,使兩個必須發生關係的類之間存有最大的靈活性。
3.1如何把握耦合
咱們應該儘量的避免實現繼承,緣由以下:
1 失去靈活性,使用具體類會給底層的修改帶來麻煩。
2 耦合問題,耦合是指兩個實體相互依賴於對方的一個量度。程序員天天都在(有意識地或者無心識地)作出影響耦合的決定:類耦合、API耦合、應用程序耦合等等。在一個用擴展的繼承實現系統中,派生類是很是緊密的與基類耦合,並且這種緊密的鏈接多是被不指望的。如B extends A ,當B不全用A中的全部methods時,這時候,B調用的方法可能會產生錯誤!
咱們必須客觀的評價耦合度,系統之間不可能老是鬆耦合的,那樣確定什麼也作不了。
3.2咱們決定耦合程度的依據是什麼
簡單的說,就是根據需求的穩定性,來決定耦合的程度。對於穩定性高的需求,不容易發生變化的需求,咱們徹底能夠把各種設計成緊耦合的(咱們雖然討論類之間的耦合度,但其實功能塊、模塊、包之間的耦合度也是同樣的),由於這樣能夠提升效率,並且咱們還可使用一些更好的技術來提升效率或簡化代碼,例如c# 中的內部類技術。但是,若是需求極有可能變化,咱們就須要充分的考慮類之間的耦合問題,咱們能夠想出各類各樣的辦法來下降耦合程度,可是概括起來,不外乎增長抽象的層次來隔離不一樣的類,這個抽象層次能夠是抽象的類、具體的類,也能夠是接口,或是一組的類。咱們能夠用一句話來歸納下降耦合度的思想:"針對接口編程,而不是針對實現編程。
在咱們進行編碼的時候,都會留下咱們的指紋,如public的多少,代碼的格式等等。咱們能夠耦合度量評估從新構建代碼的風險。由於從新構建其實是維護編碼的一種形式,維護中遇到的那些麻煩事在從新構建時一樣會遇到。咱們知道在從新構建以後,最多見的隨機bug大部分都是不當耦合形成的 。
若是不穩定因素越大,它的耦合度也就越大。
某類的不穩定因素=依賴的類個數/被依賴的類個數
依賴的類個數= 在編譯此類的時被編譯的其它類的個數總和
3.3怎樣將大系統拆分紅效系統
解決這個問題的一個思路是將許多類集合成一個更高層次的單位,造成一個高內聚、低耦合的類的集合,這是咱們設計過程當中應該着重考慮的問題!
耦合的目標是維護依賴的單向性,有時咱們也會須要使用壞的耦合。在這種狀況下,應當當心記錄下緣由,以幫助往後該代碼的用戶瞭解使用耦合真正的緣由。
4.怎樣作到依賴倒轉
以抽象方式耦合是依賴倒轉原則的關鍵。抽象耦合關係總要涉及具體類從抽象類繼承,而且須要保證在任何引用到基類的地方均可以改換成其子類,所以,里氏代換原則是依賴倒轉原則的基礎。
在抽象層次上的耦合雖然有靈活性,但也帶來了額外的複雜性,若是一個具體類發生變化的可能性很是小,那麼抽象耦合能發揮的好處便十分有限,這時能夠用具體耦合反而會更好。
層次化:全部結構良好的面向對象構架都具備清晰的層次定義,每一個層次經過一個定義良好的、受控的接口向外提供一組內聚的服務。
依賴於抽象:建議不依賴於具體類,即程序中全部的依賴關係都應該終止於抽象類或者接口。儘可能作到:
一、任何變量都不該該持有一個指向具體類的指針或者引用。
二、任何類都不該該從具體類派生。
三、任何方法都不該該覆寫它的任何基類中的已經實現的方法。
5.依賴倒轉原則的優缺點
依賴倒轉原則雖然很強大,但卻最不容易實現。由於依賴倒轉的緣故,對象的建立極可能要使用對象工廠,以免對具體類的直接引用,此原則的使用可能還會致使產生大量的類,對不熟悉面向對象技術的工程師來講,維護這樣的系統須要較好地理解面向對象設計。
依賴倒轉原則假定全部的具體類都是會變化的,這也不老是正確。有一些具體類多是至關穩定,不會變化的,使用這個具體類實例的應用徹底能夠依賴於這個具體類型,而沒必要爲此建立一個抽象類型。
此部分引用自:http://www.cnblogs.com/temptation/archive/2008/03/10/1098351.html
四.接口隔離原則(Interface Segregation Principle, ISP)
在講接口隔離原則以前,先明確一下咱們的主角——接口。接口分爲兩種:
◇ 實例接口(Object Interface),在Java中聲明一個類,而後用new關鍵字產生的一個實例,它是對一個類型的事物的描述,這是一種接口,好比你定義Person這個類,而後使用Person zhangSan = new Person()產生了一個實例,這個實例要聽從的標準就是Person這個類,Person類就是zhangSan的接口,疑惑?看不懂?沒關係,那是由於讓Java語言浸染的時間太長了,只要知道從這個角度來看,Java中的類也是一種接口;
◇ 類接口(Class Interface),Java中常用的interface關鍵字定義的接口。
主角已經定義清楚了,那什麼是隔離呢?它有兩種定義,以下所示:
◇ 「Clients should not be forced to depend upon interfaces that they don't use」——客戶端不該該依賴它不需用的接口。
◇ 「The dependency of one class to another one should depend on the smallest possible interface」——類間的依賴關係應該創建在最小的接口上。
新事物的定義通常都比較難理解,晦澀難懂是正常的。咱們把這兩個定義剖析一下,先說第一種定義:「客戶端不該該依賴它不須要接口」,那依賴什麼?依賴它須要的接口,客戶端須要什麼接口就提供什麼接口,把不須要的接口剔除掉,那就須要對接口進行細化,保證其純潔性;再看第二個定義:「類間的依賴關係應該創建在最小的接口上」,它要求是最小的接口,也是要求接口細化,接口純潔,與第一個定義一模一樣,只是一個事物的兩種不一樣描述。
咱們能夠把這兩個定義歸納爲一句話:創建單一接口,不要創建臃腫龐大的接口。再通俗一點講:接口儘可能細化,同時接口中的方法儘可能少。看到這裏你們有可能要疑惑了,這與單一職責原則不是相同的嗎?錯,接口隔離原則與單一職責的審視角度是不相同的,單一職責要求的是類和接口職責單一,注重的是職責,這是業務邏輯上的劃分,而接口隔離原則要求接口的方法儘可能少。例如一個接口的職責可能包含10個方法,這10個方法都放在一個接口中,而且提供給多個模塊訪問,各個模塊按照規定的權限來訪問,在系統外經過文檔約束「不使用的方法不要訪問」,按照單一職責原則是容許的,按照接口隔離原則是不容許的,由於它要求「儘可能使用多個專門的接口」,專門的接口指什麼?就是指提供給每一個模塊都應該是單一接口,提供給幾個模塊就應該有幾個接口,而不是創建一個龐大的臃腫的接口,容納全部的客戶端訪問。
五.合成/聚合複用原則(Composite/Aggregate Reuse Principle,CARP)
5.1 概念
定義:在一個新的對象裏面使用一些已有的對象,使之成爲新對象的一部分;新的對象經過向這些對象的委派達到複用這些對象的目的。應首先使用合成/聚合,合成/聚合則使系統靈活,其次才考慮繼承,達到複用的目的。而使用繼承時,要嚴格遵循里氏代換原則。有效地使用繼承會有助於對問題的理解,下降複雜度,而濫用繼承會增長系統構建、維護時的難度及系統的複雜度。
若是兩個類是「Has-a」關係應使用合成、聚合,若是是「Is-a」關係可以使用繼承。"Is-A"是嚴格的分類學意義上定義,意思是一個類是另外一個類的"一種"。而"Has-A"則不一樣,它表示某一個角色具備某一項責任。
5.2 什麼是合成?什麼是聚合?
合成(Composition)和聚合(Aggregation)都是關聯(Association)的特殊種類。
聚合表示總體和部分的關係,表示「擁有」。如奔馳S360汽車,對奔馳S360引擎、奔馳S360輪胎的關係是聚合關係,離開了奔馳S360汽車,引擎、輪胎就失去了存在的意義。在設計中, 聚合不該該頻繁出現,這樣會增大設計的耦合度。
合成則是一種更強的「擁有」,部分和總體的生命週期同樣。合成的新的對象徹底支配其組成部分,包括它們的建立和湮滅等。一個合成關係的成分對象是不能與另外一個合成關係共享的。
換句話說,合成是值的聚合(Aggregation by Value),而通常說的聚合是引用的聚合(Aggregation by Reference)。
明白了合成和聚合關係,再來理解合成/聚合原則應該就清楚了,要避免在系統設計中出現,一個類的繼承層次超過3層,則需考慮重構代碼,或者從新設計結構。固然最好的辦法就是考慮使用合成/聚合原則。
5.3 經過合成/聚合來進行復用的優缺點
優勢:
1) 新對象存取成分對象的惟一方法是經過成分對象的接口。
2) 這種複用是黑箱複用,由於成分對象的內部細節是新對象所看不見的。
3) 這種複用支持包裝。
4) 這種複用所需的依賴較少。
5) 每個新的類能夠將焦點集中在一個任務上。
6) 這種複用能夠在運行時間內動態進行,新對象能夠動態的引用與成分對象類型相同的對象。
7) 做爲複用手段能夠應用到幾乎任何環境中去。
缺點:就是系統中會有較多的對象須要管理。
5.4 經過繼承來進行復用的優缺點
優勢:
1)新的實現較爲容易,由於超類的大部分功能能夠經過繼承的關係自動進入子類。
2)修改和擴展繼承而來的實現較爲容易。
缺點:
1) 繼承複用破壞包裝,由於繼承將超類的實現細節暴露給子類。因爲超類的內部細節經常是對於子類透明的,因此這種複用是透明的複用,又稱「白箱」複用。
2) 若是超類發生改變,那麼子類的實現也不得不發生改變。
3)從超類繼承而來的實現是靜態的,不可能在運行時間內發生改變,沒有足夠的靈活性。
4)繼承只能在有限的環境中使用。
此部分引用自:http://www.cnblogs.com/temptation/archive/2008/03/10/1098351.html
六.迪米特法則(Law of Demeter LoD)
5.1概述
定義:一個軟件實體應當儘量少的與其餘實體發生相互做用。
這樣,當一個模塊修改時,就會盡可能少的影響其餘的模塊。擴展會相對容易。
這是對軟件實體之間通訊的限制。它要求限制軟件實體之間通訊的寬度和深度。
5.2迪米特法則的其餘表述:
1)只與你直接的朋友們通訊。
2)不要跟「陌生人」說話。
3)每個軟件單位對其餘的單位都只有最少的知識,並且侷限於那些與本單位密切相關的軟件單位。
5.3狹義的迪米特法則
若是兩個類沒必要彼此直接通訊,那麼這兩個類就不該當發生直接的相互做用。若是其中的一個類須要調用另外一個類的某一個方法的話,能夠經過第三者轉發這個調用。
朋友圈的肯定
「朋友」條件:
1)當前對象自己(this)
2)以參量形式傳入到當前對象方法中的對象
3)當前對象的實例變量直接引用的對象
4)當前對象的實例變量若是是一個彙集,那麼彙集中的元素也都是朋友
5)當前對象所建立的對象
任何一個對象,若是知足上面的條件之一,就是當前對象的「朋友」;不然就是「陌生人」。
缺點:會在系統裏造出大量的小方法,散落在系統的各個角落。
與依賴倒轉原則互補使用
5.4狹義的迪米特法則的缺點:
在系統裏造出大量的小方法,這些方法僅僅是傳遞間接的調用,與系統的商務邏輯無關。
遵循類之間的迪米特法則會是一個系統的局部設計簡化,由於每個局部都不會和遠距離的對象有直接的關聯。可是,這也會形成系統的不一樣模塊之間的通訊效率下降,也會使系統的不一樣模塊之間不容易協調。
5.5迪米特法則與設計模式
門面(外觀)模式和調停者(中介者)模式實際上就是迪米特法則的具體應用。
5.6廣義的迪米特法則
迪米特法則的主要用意是控制信息的過載。在將迪米特法則運用到系統設計中時,要注意下面的幾點:
1)在類的劃分上,應當建立有弱耦合的類。
2)在類的結構設計上,每個類都應當儘可能下降成員的訪問權限。
3)在類的設計上,只要有可能,一個類應當設計成不變類。
4)在對其餘類的引用上,一個對象對其對象的引用應當降到最低。
5.7廣義迪米特法則在類的設計上的體現
1)優先考慮將一個類設置成不變類
2)儘可能下降一個類的訪問權限
3)謹慎使用Serializable
4)儘可能下降成員的訪問權限
5)取代C Struct
迪米特法則又叫做最少知識原則(Least Knowledge Principle或簡寫爲LKP),就是說一個對象應當對其餘對象有儘量少的瞭解。
5.8如何實現迪米特法則
迪米特法則的主要用意是控制信息的過載,在將其運用到系統設計中應注意如下幾點:
1) 在類的劃分上,應當建立有弱耦合的類。類之間的耦合越弱,就越有利於複用。
2) 在類的結構設計上,每個類都應當儘可能下降成員的訪問權限。一個類不該當public本身的屬性,而應當提供取值和賦值的方法讓外界間接訪問本身的屬性。
3) 在類的設計上,只要有可能,一個類應當設計成不變類。
4) 在對其它對象的引用上,一個類對其它對象的引用應該降到最低。
七.單一職責原則(Simple responsibility pinciple SRP)
一個類,應該只有一個職責。每個職責都是變化的一個軸線,若是一個類有一個以上的職責,這些職責就耦合在了一塊兒。這會致使脆弱的設計。當一個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一塊兒,會影響複用性。咱們可能只須要複用該類的某一個職責,但這個職責跟其它職責耦合在了一塊兒,很難分離出來,即第7章提到的牢固性。
SRP中,把職責定義爲「變化的緣由」。若是你能想到N個動機去改變一個類,那麼這個類就具備多於一個的職責。這裏說的「變化的緣由」,只有實際發生時纔有意義。可能預測到會有多個緣由引發這個類的變化,但這僅僅是預測,並無真的發生,這個類仍可看作具備單一職責,不須要分離職責。若是分離,會帶來沒必要要的複雜性。
若是發現一個類有多於一個的職責,應該儘可能解耦。若是很難解耦,也要分離接口,在概念上解耦。
衆所周知,OOPL能夠提升程序的封裝性、複用性、可維護性。但僅僅是「能夠」。能不能實現OOPL的這些優勢,要看具體怎麼作。若是一個類的代碼很是混亂,各類功能的代碼都混在一塊兒,封裝性、複用性、可維護性無從談起。
SRP是全部原則中最簡單的,也是最基本的一個。運用這個原則,能夠提升類的內聚性,有助於充分發揮OOPL的優點。