設計模式-6大設計原則
一、單一職責原則(SRP)
定義:應該有且只有一個原因引起類的變更
二、里氏替換原則(LSP)
定義:所有引用基類的地方必須能透明地使用其子類的對象。
即只要父類出現的地方子類就可以出現,而且替換爲子類也不會產生任何錯誤和異常,使用者可能根本不需要知道是父類還是子類,但是反過來不可以,有子類出現的地方,父類未必可以使用
這個定義包含了4層含義
1.子類必須完全實現父類的方法
子類必須完全實現父類方法,不然會導致父類對象調用是產生異常,如
interface IHandle{
void method1();
void method2();
}
class ImpHandleA implements IHandle {
@Override
public void method1() {
System.out.println("ImpHandleA method1");
}
@Override
public void method2() {
System.out.println("ImpHandleA method2");
}
}
class ImpHandleB implements IHandle {
@Override
public void method1() {
System.out.println("ImpHandleB method1");
}
@Override
public void method2() {
throw new IllegalStateException("不允許的方法調用");
}
}
public static void main(String[] args) {
IHandle handle = new ImpHandleA();
handle.method1();
handle.method2();
handle = new ImpHandleB();
handle.method1();
//此時調用method2出錯
handle.method2();
}
如上代碼,ImpHandleB未完全實現IHandle接口,導致ImpHandleB的實例調用method2時報錯,或者ImpHandleB只做了空實現如
@Override
public void method2() {
}
這樣調用代碼雖然不再報錯,但卻無法達到需要的執行效果,如果在剛方法執行後面有依賴於該方法執行結果的代碼,則會產生未知的錯誤;
所以,如果子類不能完整地實現父類的方法,或者父類的某些方法在子類已經發生“畸變”,則建議斷開父子繼承關係,採用依賴、組合、聚集等你關係代替。
2.子類可以有自己的個性
子類可以有自己的方法和屬性,所以里氏替換原則可以正着用,不能反着用,如
class SupperClass {
public void method() {
System.out.println("SupperClass method1");
}
}
class SubClass extends SupperClass {
@Override
public void method() {
System.out.println("SubClass method");
}
public void otherMethod() {
System.out.println("SubClass otherMethod");
}
}
public static void main(String[] args) {
SupperClass supperClass = new SupperClass();
supperClass.method();
//此處運行報錯
((SubClass) supperClass).otherMethod();
}
不能將父類實例轉爲子類實例
3.覆蓋或實現父類的方法時輸入參數可以被放大
即子類的方法 參數類型可以爲父類的方法 參數類型 的父類
class Father {
public void method(HashMap map) {
System.out.println("父類方法被執行");
}
}
class Son extends Father {
public void method(Map map) {
System.out.println("子類方法被執行");
}
}
public static void main(String[] args) {
Father father=new Father;
HashMap map=new HashMap();
father.method(map);
// 輸出:父類方法被執行
Son son=new Son();
son.method(map);
//同樣輸出:父類方法被執行
//符合里氏替換原則,所有引用基類的地方必須能透明地使用其子類的對象
}
而如果反過來
class Father {
public void method(Map map) {
System.out.println("父類方法被執行");
}
}
class Son extends Father {
public void method(HashMap map) {
System.out.println("子類方法被執行");
}
}
public static void main(String[] args) {
Father father=new Father;
HashMap map=new HashMap();
father.method(map);
// 輸出:父類方法被執行
Son son=new Son();
son.method(map);
//輸出:子類方法被執行
//不再符合里氏替換原則,子類在沒有複寫父類方法的前提下,子類方法被執行了,會引起邏輯混亂
}
4.覆寫或實現父類的方法時輸出結果可以被縮小
即子類方法返回的類型 可以是父類方法返回類型的 子類型
三、依賴倒置原則(DIP)
定義:
-
高層模塊不應該依賴底層模塊,兩者都應該依賴其抽象
-
抽象不應該依賴細節;
-
細節應該依賴抽象
更加精簡的定義爲:面向接口編程
依賴倒置原則的本質就是通過抽象使各個類或模塊的實現彼此獨立,不互相影響,實現模塊的松耦合。
要想實現上述規則,只要遵循以下幾個原則
-
每個類儘量都有接口或抽象類,或者抽象類和接口兩者都具備
-
變量的表面類型儘量都是接口或者是抽象類
-
任何類都不應該從具體類派生
-
儘量不要覆寫基類的方法
-
綜合里氏替換原則使用
四、接口隔離原則
定義:客戶端不應該依賴他不需要的接口, 類間的依賴關係應該建立在最小的接口上。
可以將上面概括爲:建立單一接口,不要建立臃腫龐大的接口。接口要儘量細化,同時接口中的方法儘量少(想一下單一職責原則)
使用原則
- 一個接口只服務與一個子模塊或業務邏輯
- 通過業務邏輯壓縮接口中的public方法,接口時常去回顧,儘量不要讓藉口有一大堆方法
- 已經被污染了的接口,儘量去修改,若變更的風險較大,則採用適配器模式進行轉化處理
- 瞭解環境,拒絕盲從。具體問題具體分析,不照搬照抄
五、迪米特法則(LOD)=最少知識原則(LKP)
定義:一個對象應該對其他對象有最少的瞭解。
一個類應該對自己需要耦合或調用的類知道得最少,被調用或耦合的類 的內部是如何複雜都與調用者沒有關係,調用類只要知道,調用輸入什麼,調用完返回什麼就可以了
使用原則:
- 只和朋友(有耦合的類)交流,不關係其他的類
- 朋友間也是有距離的。不需要知道朋友的所有細節
- 是自己的就是自己。如果一個方法放在本類中,即不增加類間關係,也對本類不產生負面影響,那就放在本類中。
- 謹慎使用Serializable。
六、開閉原則
定義:一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。
理解:當類完成編寫後,後續的功能添加等應該儘量用繼承之類的方式而不是在已經完成的類裏修改