天天看點

設計模式六大原則1.開放封閉原則2.單職責原則3.裡氏代換原則4.依賴倒置原則5.迪米特法則6.接口隔離原則7.參考文章

1.開放封閉原則

一個軟體實體應當對擴充開放,則修改關閉。是以在設計一個子產品時,應當使得這個子產品可以在不被修改的前提下被擴充。一個軟體産品在生命周期内,都會發生變化,既然變化是一個既定的事實,我們就應該在設計的時候盡量适應這些變化,以提高項目的穩定性和靈活性。

設計的目的便在于:面對需求的改變而保持系統的相對穩定,進而使得系統可以很容易的從一個版本更新到另一個版本。

開放封閉原則的實作

開閉原則其實在大話設計模式中說得非常好,讓人通俗易懂。它舉了一個例子,我覺得說得非常好。是加減乘除法的例子。開始需求是做一個加法的操作。後來繼續加入減法、乘法、除法。

有了加法以後可能會做一個需求變更:加入其它的算法法則。是以我們要有一個預判性,這個預判性會導緻我們項目以後的擴充性,也會導緻如果需求發生變更,程式修改的難易程度。是以,我們要做一個算法法則的操作類,加減乘除法都繼承此操作接口。再加一個算法法則的用戶端類類操作此算法。

設計模式六大原則1.開放封閉原則2.單職責原則3.裡氏代換原則4.依賴倒置原則5.迪米特法則6.接口隔離原則7.參考文章

開放封閉原則的特點

  • 1.通過擴充已有的軟體系統,可以提供新的行為,以滿足對軟體的新需求,是變化中的軟體有一定的适應性和靈活性。
  • 2.已有的軟體子產品,特别是最重要的抽象子產品不能再修改,這就使變化中的軟體系統有一定的穩定性和延續性。

2.單職責原則

單一職責原則是實作高内聚、低耦合的指導方針。就一個類而言,應該僅有一個引起它變化的原因。

每一個職責都是一個變化的軸線,當需求變化時會反映為類的職責的變化。如果一個類承擔的職責多于一個,那麼引起它變化的原因就有多個。一個職責的變化甚至可能會削弱或者抑制類完成其他職責的能力,進而導緻脆弱的設計。

示例:假如有類Class1完成職責T1,T2,當職責T1或T2有變更需要修改時,有可能影響到該類的另外一個職責正常工作。

好處:類的複雜度降低、可讀性提高、可維護性提高、擴充性提高、降低了變更引起的風險。

3.裡氏代換原則

所有引用基類(父類)的地方必須能透明地使用其子類的對象。而且它察覺不出父類和子類對象的差別。也就是說,在軟體裡面,把父類替換成它的子類,程式的行為沒有變化。

裡氏替換原則告訴我們不要破壞繼承體系

繼承優點

代碼共享,減少建立類的工作量,每個子類都擁有父類的方法和屬性;提高代碼的重用性;子類可以形似父類,但又異于父類;提高代碼的可擴充性,實作父類的方法就可以“為所欲為”了;提高産品或項目的開放性。

繼承缺點

繼承是侵入性的。隻要繼承,就必須擁有父類的所有屬性和方法;降低代碼的靈活性。子類必須擁有父類的屬性和方法;增強了耦合性。當父類的常量、變量和方法被修改時,必需要考慮子類的修改,而且在缺乏規範的環境下,這種修改可能帶來非常糟糕的結果;大片的代碼需要重構。

示例:克服繼承的缺點——裡氏替換原則

這裡參考大佬的文章https://blog.csdn.net/zhengzhb/article/details/7281833

我們需要完成一個兩數相減的功能,由類A來負責。

class A{
	public int func1(int a, int b){
		return a-b;
	}
}
 
public class Client{
	public static void main(String[] args){
		A a = new A();
		System.out.println("100-50="+a.func1(100, 50));
		System.out.println("100-80="+a.func1(100, 80));
	}
}

運作結果:

100-50=50
100-80=20
           

後來,我們需要增加一個新的功能:完成兩數相加,然後再與100求和,由類B來負責。即類B需要完成兩個功能:

  • 兩數相減。
  • 兩數相加,然後再加100。

由于類A已經實作了第一個功能,是以類B繼承類A後,隻需要再完成第二個功能就可以了,代碼如下:

class B extends A{
	public int func1(int a, int b){
		return a+b;
	}
	
	public int func2(int a, int b){
		return func1(a,b)+100;
	}
}
 
public class Client{
	public static void main(String[] args){
		B b = new B();
		System.out.println("100-50="+b.func1(100, 50));
		System.out.println("100-80="+b.func1(100, 80));
		System.out.println("100+20+100="+b.func2(100, 20));
	}
}

類B完成後,運作結果:

100-50=150
100-80=180
100+20+100=220
           

我們發現原本運作正常的相減功能發生了錯誤。原因就是類B在給方法起名時無意中重寫了父類的方法,造成所有運作相減功能的代碼全部調用了類B重寫後的方法,造成原本運作正常的功能出現了錯誤。 在本例中,引用基類A完成的功能,換成子類B之後,發生了異常。在實際程式設計中,我們常常會通過重寫父類的方法來完成新的功能,這樣寫起來雖然簡單,但是整個繼承體系的可複用性會比較差,特别是運用多态比較頻繁時,程式運作出錯的幾率非常大。如果非要重寫父類的方法,比較通用的做法是:原來的父類和子類都繼承一個更通俗的基類,原有的繼承關系去掉,采用依賴、聚合,組合等關系代替。

在使用裡氏代換原則時需要注意如下幾個問題:

  • (1)子類的所有方法必須在父類中聲明,或子類必須實作父類中聲明的所有方法。 根據裡氏代換原則,為了保證系統的擴充性,在程式中通常使用父類來進行定義,如果一個方法隻存在子類中,在父類中不提供相應的聲明,則無法在以父類定義的對象中使用該方法。
  • (2) 我們在運用裡氏代換原則時,盡量把父類設計為抽象類或者接口,讓子類繼承父類或實作父接口,并實作在父類中聲明的方法,運作時,子類執行個體替換父類執行個體,我們可以很友善地擴充系統的功能,同時無須修改原有子類的代碼,增加新的功能可以通過增加一個新的子類來實作。裡氏代換原則是開閉原則的具體實作手段之一。
  • (3) Java語言中,在編譯階段,Java編譯器會檢查一個程式是否符合裡氏代換原則,這是一個與實作無關的、純文法意義上的檢查,但Java編譯器的檢查是有局限的

4.依賴倒置原則

依賴倒置原則告訴我們要面向接口程式設計;

核心思想:高層子產品不應該依賴底層子產品,二者都該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象;

說明:高層子產品就是調用端,低層子產品就是具體實作類。抽象就是指接口或抽象類。細節就是實作類。

好處:依賴倒置的好處在小型項目中很難展現出來。但在大中型項目中可以減少需求變化引起的工作量。使并行開發更友好。

怎樣做到依賴倒轉

1.工廠方法模式

2.模闆方法模式

3.疊代器模式

設計模式詳解傳送門https://mp.weixin.qq.com/s/AMJ0h4s8kOOSBBDlwN0nlw

5.迪米特法則

迪米特法則告訴我們要降低耦合。迪米特法則(Law of Demeter )又叫做最少知識原則,也就是說,一個對象應當對其他對象盡可能少的了解。

迪米特法則最初是用來作為面向對象的系統設計風格的一種法則,于1987年秋天由lan holland在美國東北大學為一個叫做迪米特的項目設計提出的。

迪米特法則實作方式:

1.外觀模式

2.中介者模式

核心思想:類間解耦。

通俗來講: 一個類對自己依賴的類知道的越少越好。自從我們接觸程式設計開始,就知道了軟體程式設計的總的原則:低耦合,高内聚。無論是面向過程程式設計還是面向對象程式設計,隻有使各個子產品之間的耦合盡量的低,才能提高代碼的複用率。低耦合的優點不言而喻,但是怎麼樣程式設計才能做到低耦合呢?那正是迪米特法則要去完成的。

6.接口隔離原則

開閉原則是總綱,他告訴我們要對擴充開放,對修改關閉。接口隔離原則告訴我們在設計接口的時候要精簡單一;

  • 1.不應該強行要求用戶端依賴于它們不用的接口;
  • 2.類之間的依賴應該建立在最小的接口上面。簡單點說,用戶端需要什麼功能,就提供什麼接口,對于用戶端不需要的接口不應該強行要求其依賴;
  • 3.類之間的依賴應該建立在最小的接口上面,這裡最小的粒度取決于單一職責原則的劃分。

7.參考文章

https://blog.csdn.net/yucaixiang/article/details/90239817

https://www.jianshu.com/p/807bc228dbc2

繼續閱讀