1. 程式人生 > 其它 >設計模式:簡介、OOP七大設計原則

設計模式:簡介、OOP七大設計原則

23種設計模式

簡介

設計模式(Design pattern)是對軟體設計中普遍存在問題的解決方案。

這些解決方案是眾多軟體開發人員經過相當長的一段時間的試驗和錯誤總結出來的。

目的

  1. 提高思維、程式設計和設計能力;
  2. 使程式設計更標準化、程式碼編制更工程化,提高開發效率,縮短開發週期;
  3. 使程式碼具有更好的:重用性、可讀性、可擴充套件性(可維護性)、可靠性、高內聚低耦合

設計模式(GoF23)

  • 建立型
    • 單例模式、工廠模式、抽象工廠模式、建造者模式、原型模式;
  • 結構型
    • 介面卡模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式
  • 行為型
    • 模板方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、直譯器模式、狀態模式、策略模式、職責鏈模式、訪問者模式。

OOP七大設計原則

在學習設計原則和設計模式之前,需要先了解 UML:類圖及類的關係

設計原則

  • 設計模式的基礎,即設計的依據;
  • 封裝繼承多型,以及類的關聯關係和組合關係的充分理解。
  1. 單一職責原則 (Singel Responsibility)
  2. 介面隔離原則 (Interface Segregation)
  3. 合成複用原則 (Composite Reuse)
  4. 裡式替換原則 (Liskov Substitution)
  5. 依賴倒置原則 (Dependence Inversion)
  6. 迪米特法則 (Demeter)
  7. 開閉原則:對擴充套件開放,對修改關閉;

1、單一職責(Single Responsibility)

簡介

  • 降低類的複雜度,一個類只負責一項職責
  • 提高類的可讀性、可維護性,降低變更引起的風險;
  • 分解類的粒度,高內聚低耦合;

例項

  • 類A:負責 職責1職責2
  • 問題:當修改 職責1 的程式碼時,可能對 職責2 的程式碼造成影響;
  • 解決:將 類A 的粒度分解為A1,A2(即分為兩個類)。

2、介面隔離(Interface Segregation)

簡介

  • 客戶端不應該依賴它不需要的介面,即類對另一個類的依賴應建立在最小的介面上
  • 為各個類建立它們需要的專門介面;

例項

  • 介面F:有 5 個方法(Method1,2,3,4,5),2 個實現類(FImpl1FImpl2
  • 類A:通過介面F 依賴(使用)類FImpl1 中的 3 個方法(Method1,2,3
  • 類B:通過介面F 依賴(使用)類FImpl2 中的 3 個方法(Method1,4,5
  • 問題:介面F 對於類A 和 類B 來說不是最小介面,則實現類FImpl1 和 類FImpl2 需要實現它們不需要的方法;

  • 解決:將介面F 拆分為獨立的 3 個介面,類A 和 類B 分別與需要的介面建立依賴關係。

3、合成複用(Composite Reuse)

優先使用聚合 / 組合關係,其次考慮繼承關係。

4、裡式替換(Liskov Substitution)

簡介

  1. 引用父類的地方,必須能透明地使用其子類的物件。即繼承必須確保父類的性質在子類中成立
  2. 子類儘量不要重寫父類方法,否則會降低繼承體系的複用性,特別是對於多型的使用;
  3. 繼承會提高耦合性,通用做法:改用依賴/聚合/組合等關係來替換繼承關係;

例項

  • 父類Calc1:有方法 operation1(int x, int y),返回兩數之和
  • 子類Calc2:繼承父類Calc1
    • (可能是無意間)重寫方法operation1(),返回兩數之差;
    • 新增新方法operation2(int x, int y):返回兩數之積
  • 問題:在引用父類時使用多型Calc1 calc = new Calc2()]】,呼叫方法operation1() 欲計算兩數之和,而實際執行時返回兩數之差;

  • 解決:將原來的父類和子類都繼承一個更基礎的基類BaseCalc,改用組合關係來代替繼承關係。

5、依賴倒置(Dependence Inversion)

簡介

  1. 中心思想面向介面程式設計,而不是面向實現程式設計;
  2. 變數的宣告型別是抽象類或介面(在變數引用和實際物件之間“加一層”)
  3. 注意
    • 低層模組最好有抽象類或介面;
    • 高層模組不依賴低層模組,二者都應依賴其抽象;
    • 抽象不依賴於細節,細節應依賴於抽象;
    • 繼承:遵循裡式替換原則

例項

  • Person類:實現接收訊息的功能;
  • Email類:接收電子郵件訊息;
  • 問題:若增加需求(接收微信、QQ訊息),需要新增對應的類和Person中對應接收方法;

  • 解決:宣告介面 MessageReceiver,訊息接收類(Email、微信、QQ)實現該介面,則Person類只需與介面建立依賴關係即可

依賴注入方式(!)

3 種依賴注入方式

  • Spring IOC DI:構造器注入、setter注入
  1. 構造器注入:宣告變數時,向構造方法傳遞引數;
  2. setter注入:宣告變數後,通過 setter設定引數;
  3. 呼叫時注入:呼叫方法時,向方法傳遞引數;

例項

// 待注入介面
interface TV{...}

// 1、構造器注入
interface mySwitch{
    private TV tv;
    public mySwitch(TV tv){
        this.tv = tv;
    }
}
// 2、setter注入
interface mySwitch{
    private TV tv;
    public void setTv(Tv tv){
        this.tv = tv;
    }
    
// 3、呼叫時注入
interface mySwitch{
    public void turnOn(TV tv);
	}
}

6、迪米特(Demeter)

簡介

  1. 迪米特法則(又稱“最少知道原則”),即一個類對自己依賴的類知道的越少越好;
  2. 被依賴的類:程式碼邏輯都封裝在類內部,對外只提供 public方法。
  3. 更簡單的定義只與直接朋友交流
    • 類的依賴關係:成員變數(聚合 / 組合)、方法引數、方法返回值、區域性變數
    • 直接朋友:成員變數(聚合 / 組合)、方法引數、方法返回值;
    • 陌生朋友區域性變數

例項

  • Student類:屬性 (id);
  • Teacher類:屬性 (id);
  • StudentManage類:方法 (獲取所有學生資訊);
  • TeacherManage類:方法(獲取所有老師、輸出所有老師和學生的資訊)
  • 問題:TeacherManage類中的 “輸出所有學生”方法,使用到了成員變數Student(陌生朋友),違背迪米特法則;

  • 解決:將 “輸出所有學生”方法,改為在 StudentManage類中宣告。而TeacherManage類中通過方法引數呼叫StudentManage類中的 “輸出所有學生”方法。

7、開閉(Open Close)