1. 程式人生 > 實用技巧 >設計模式七大原則

設計模式七大原則

設計模式七大原則

  1. 單一職責原則
  • 對類來說,一個類只負責一項職責。比如說A類有兩個職責,職責1,職責2;那麼就可以把A類分為兩個類,這就是遵循了單一職責原則
  1. 介面隔離原則
  • 客戶端類不需要依賴它不需要的介面,既一個類對另一類依賴應該建立在最小介面上
  • 簡單的說客戶類,要使用一個介面的時候,不要把用不到的方法也實現了
  1. 依賴倒轉原則
  • 高層模組不要依賴底層模組,二者都要依賴其抽象
  • 抽象不要依賴細節,細節應該依賴抽象
  • 依賴倒轉(倒置)的中心思想就是面向介面程式設計
  • 依賴倒轉原則基於這樣的設計理念;對於細節的多邊形,抽象要穩定的多。以抽象為基礎搭建的架構要比以細節為基礎的架構穩定的多。在Java中抽象就是介面或抽象類,細節就是實體類
  • 使用介面或抽象類目的是指定好的規範,而不涉及任何具體操作。把展示細節部分交給他們的實現類完成
  1. 里氏替換原則
  • 在繼承上,儘量不要重寫父類的方法
  • 適當情況下,可以使用組合、聚合來代替繼承
  • 可以使用父類的地方也可以使用子類
  1. 開閉原則OCP
  • 開閉原則是最重要的原則
  • 對擴充套件開發;對修改關閉。
  • 對於增加服務的時候,不會影響已使用的程式碼;
  1. 迪米特法則
  2. 合成複用原則

設計模式目的

  • 程式碼重用性
  • 可讀性
  • 可擴充套件性
  • 可靠性
  • 使程式高內聚,低耦合

單一職責原則

說明
對類來說,一個類只負責一項職責。比如說A類有兩個職責,職責1,職責2;那麼就可以把A類分為兩個類,這就是遵循了單一職責原則
程式碼演示的升級變化

/**
 * @author HiWin10
 * @date 2020/7/29
 * 學習單一職責原則,使用交通工具來進行舉例,
  * 該版本類,是沒有遵循單一職責原則的,應該這個類的公交類很複雜,使用了很多交通工具,解決辦法是,根據交通工具的類別分別建立對應的類
  */ public class SingleResponsibility1 {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
  vehicle.run("摩托車");
  vehicle.run("飛機");
  vehicle.run("輪船");
  }
}

/**
 * 交通工具類
  * 沒有遵循單一職責原則,解決辦法是,根據交通工具分別建立對應類  具體看SingleResponsibility2
 */ class Vehicle {
    public void run (String vehicle) {
        System.out.println(vehicle + "在公路上執行....");
  }
}
/**
 * @author HiWin10
 * @date 2020/7/29
 * 1. 遵守了單一職責原則,
  * 2. 但是這樣改動很大,將類分解,還要修改客戶端
  * 3. 直接修改Vehicle,對於咱們目前的場景,這個類只有一個功能的話,那麼可以有方法單一職責具體看SingleResponsibility3,
 * 當然SingleResponsibility3具體要看需求的
  */ public class SingleResponsibility2 {
    public static void main(String[] args) {
        RoadVehicle roadVehicle = new RoadVehicle();
  roadVehicle.run("摩托");
  AriVehicle ariVehicle = new AriVehicle();
  ariVehicle.run("飛機");
  WaterVehicle waterVehicle = new WaterVehicle();
  waterVehicle.run("輪船");
  }
}

class RoadVehicle {
    public void run (String vehicle) {
        System.out.println(vehicle + "在公路上執行....");
  }
}

class AriVehicle {
    public void run (String vehicle) {
        System.out.println(vehicle + "在天空執行....");
  }
}

class WaterVehicle {
    public void run (String vehicle) {
        System.out.println(vehicle + "在水裡執行....");
  }
}
/**
 * @author HiWin10
 * @date 2020/7/29
 * 1. 這種方式沒有對SingleResponsibility1而言沒有大的修改,只是增加方法
  * 2. 這種方式雖然對類沒有遵循單一職責原則,但是在方法上,遵循了單一職業原則
  * 3. 所以說即使是設計模式,也沒有說必須的套路,要根據實際應用來使用
  */ public class SingleResponsibility3 {
    public static void main(String[] args) {
        Vehicle3 vehicle = new Vehicle3();
  vehicle.run("摩托");
  vehicle.runAir("飛機");
  vehicle.runWater("輪船");
  }
}

class Vehicle3 {
    public void run(String vehicle) {
        System.out.println(vehicle + "在公路上跑....");
  }

    public void runAir(String vehicle) {
        System.out.println(vehicle + "在天空執行....");
  }

    public void runWater(String vehicle){
        System.out.println(vehicle + "在水中執行....");
  }
}

小結

  1. 降低類的複雜度,一個類只負責意見事情
  2. 提高程式碼的可讀性和維護性,因為一個類只做一件事情,那麼這個類可以說是很簡單
  3. 修改其他有關程式碼,不會影響其他程式碼
  4. 通常情況下,我們應對遵循單一職責原則,只有在程式碼邏輯十分簡單,可以不遵守或者可以在方法級別上遵守

介面隔離原則

概念
客戶端類不應該依賴它不要的介面,既一個類對另一個類的依賴應該建立在最小介面上
簡答的說就是不需要的類不要依賴,不要的方法不要實現。

  1. 該圖片中,A類使用B類的1,2,3方法;C類使用D類的1,4,5方法;B類和D類分別都實現了Interface1的所有方法。
  2. 那麼根據介面隔離原則來講,使用不到的不實現。也就是說B類不需要實現Interface1的4,5方法,D類不要實現Interface1的2,3方法
  3. 解決辦法:將Interface1介面中方法分離,1方法單獨一個介面,2,3單獨一個介面,4,5單獨一個介面
  4. 根據實際情況來定,這個介面隔離原則還可以使用抽象類來解決,或者普通的類也可以解決。

下面是沒有遵循介面隔離原則的場景模擬

/**
 * @author HiWin10
 * @date 2020/7/31
 * 介面隔離原則模擬
  * 介面隔離就是一個介面有很多方法,每個實現都要排除不必要的方法
  */ public class Segregation1 {
    public static void main(String[] args) {
        A a = new A();
  a.depend1(new B());
  a.depend2(new B());
  a.depend3(new B());
  System.out.println("-----------------------");
  C c = new C();
  c.depend1(new D());
  c.depend4(new D());
  c.depend5(new D());
  }
}

/**
 * 介面有5個方法,全部都要實現
  */ interface Interface1 {
    void operation1();
 void operation2();
 void operation3();
 void operation4();
 void operation5(); }

/**
 * 介面的實現,B,但是A類只會依賴B類(使用),使用B類的1,2,3,方法
  */ class B implements Interface1{
    @Override
  public void operation1() {
        System.out.println("B 實現了Interface1的 operation1方法 ");
  }

    @Override
  public void operation2() {
        System.out.println("B 實現了Interface1的 operation2方法 ");
  }

    @Override
  public void operation3() {
        System.out.println("B 實現了Interface1的 operation3方法 ");
  }

    @Override
  public void operation4() {
        System.out.println("B 實現了Interface1的 operation4方法 ");
  }

    @Override
  public void operation5() {
        System.out.println("B 實現了Interface1的 operation5方法 ");
  }
}

/**
 * 介面的實現D,但是C類只會依賴D類(使用),使用D類的1,4,5,方法
  */ class D implements Interface1 {
    @Override
  public void operation1() {
        System.out.println("D 實現了Interface1的 operation1方法 ");
  }

    @Override
  public void operation2() {
        System.out.println("D 實現了Interface1的 operation2方法 ");
  }

    @Override
  public void operation3() {
        System.out.println("D 實現了Interface1的 operation3方法 ");
  }

    @Override
  public void operation4() {
        System.out.println("D 實現了Interface1的 operation4方法 ");
  }

    @Override
  public void operation5() {
        System.out.println("D 實現了Interface1的 operation5方法 ");
  }
}

/**
 * A類通過介面依賴B類(使用B類,只會使用1,2,3方法)
 * 通過這個就可以看到,我只想使用介面的三個方法,但是B類卻實現了Interface1介面的所有方法
  * 這樣的話對於A類來說4,5方法都是使用不到的,卻實現了,從這裡看程式碼就非常多餘
  */ class A {
    public void depend1(Interface1 i){
        i.operation1();
  }
    public void depend2(Interface1 i){
        i.operation2();
  }
    public void depend3(Interface1 i){
        i.operation3();
  }
}

class C { // C類通過介面依賴D類(使用D類,只會使用1,4,5方法)
  public void depend1(Interface1 i){
        i.operation1();
  }
    public void depend4(Interface1 i){
        i.operation4();
  }
    public void depend5(Interface1 i){
        i.operation5();
  }
}

遵循介面隔離原則

package com.itcast.介面隔離原則;   /**
 * @author HiWin10
 * @date 2020/7/31
 * 介面隔離原則模擬
  * 介面隔離就是一個介面有很多方法,每個實現都要排除不必要的方法
  * 解決辦法:
  * 將幾個方法根據使用不同來拆除,比如說A類使用1,2,3方法;C類使用1,4,5方法,那麼1方法就是1個類,2,3一類,4,5一個類
  * 也可以使用抽象類解決該問題
  */ public class Segregation2 {
    public static void main(String[] args) {
        A2 a = new A2();
  a.depend1(new B2());
  a.depend2(new B2());
  a.depend3(new B2());
  System.out.println("------------------");
  C2 c = new C2();
  c.depend1(new D2());
  c.depend4(new D2());
  c.depend5(new D2());    }
}

/**
 * 將介面方法根據使用來拆除,後期實現可以根據實際情況使用抽象類解決該問題
  */ interface Interface2 {
    void operation1(); }

interface Interface3 {
    void operation2();
 void operation3(); }
interface Interface4 {
    void operation4();
 void operation5(); }

class B2 implements Interface2, Interface3 {
    @Override
  public void operation1() {
        System.out.println("B2 實現了 Interface2的operation1方法");
  }

    @Override
  public void operation2() {
        System.out.println("B2 實現了 Interface2的operation2方法");
  }

    @Override
  public void operation3() {
        System.out.println("B2 實現了 Interface2的operation3方法");
  }
}

class D2 implements Interface2, Interface4 {
    @Override
  public void operation1() {
        System.out.println("D2 實現了 Interface2的operation1方法");
  }

    @Override
  public void operation4() {
        System.out.println("D2 實現了 Interface2的operation4方法");
  }

    @Override
  public void operation5() {
        System.out.println("D2 實現了 Interface2的operation5方法");
  }
}

class A2 {
    public void depend1(Interface2 i){
        i.operation1();
  }
    public void depend2(Interface3 i){
        i.operation2();
  }
    public void depend3(Interface3 i){
        i.operation3();
  }
}

class C2 {
    public void depend1(Interface2 i){
        i.operation1();
  }
    public void depend4(Interface4 i){
        i.operation4();
  }
    public void depend5(Interface4 i){
        i.operation5();
  }
}

依賴倒轉原則

  1. 高層模組不要依賴底層模組,二者都要依賴其抽象
  2. 抽象不要依賴細節,細節應該依賴抽象
  3. 依賴倒轉(倒置)的中心思想就是面向介面程式設計
  4. 依賴倒轉原則基於這樣的設計理念;對於細節的多邊形,抽象要穩定的多。以抽象為基礎搭建的架構要比以細節為基礎的架構穩定的多。在Java中抽象就是介面或抽象類,細節就是實體類
  5. 使用介面或抽象類目的是指定好的規範,而不涉及任何具體操作。把展示細節部分交給他們的實現類完成

不遵循依賴倒轉原則的程式碼

/**
 * @author HiWin10
 * @date 2020/7/31
 * 依賴倒轉原則
  *  1. 高層模組不要依賴底層模組,二者都要依賴其抽象
  *  2. 抽象不要依賴細節,細節應該依賴抽象
  *  3. 依賴倒轉(倒置)的中心思想就是面向介面程式設計
  *  4. 依賴倒轉原則基於這樣的設計理念;對於細節的多邊形,抽象要穩定的多。
  * 以抽象為基礎搭建的架構要比以細節為基礎的架構穩定的多。在Java中抽象就是介面或抽象類,細節就是實體類
  *  5. 使用介面或抽象類目的是指定好的規範,而不涉及任何具體操作。把展示細節部分交給他們的實現類完成
  *
 * 案例:就是接受訊息的一個案例
  */ public class DependecyInversion {
    public static void main(String[] args) {
        Person person = new Person();
  person.receive(new Email());
  }
}

/**
 * 訊息類
  */ class Email {
    String getInfo(){
        return "郵箱訊息:Hello, World!";
  }
}

/**
 * 分析:
  *  1. 簡單,比較容易想到
  *  2. 缺乏靈活性,如果我們傳送Person類的訊息不是Email的了,而是微信訊息那麼該如何操作?這種寫法的話,程式碼寫死了缺乏靈活性
  * 解決思路:中間定義一個抽象層,方法引數為抽象層,實體類實現了抽象層,這樣來呼叫就會靈活的多
  */ class Person{
    public void receive(Email email){
        System.out.println(email.getInfo());
  }
}

遵循依賴倒轉原則

/**
 * @author HiWin10
 * @date 2020/7/31 */ public class DependecyInversion_OK {
    public static void main(String[] args) {
        Person1 person = new Person1();
  // 傳送電子郵箱訊息
  person.receive(new Email1());    // 傳送微信訊息
  person.receive(new Weixin());    }
}

interface IReceive {
    public String getInfo(); }

/**
 * 電子郵件訊息類
  */ class Email1 implements IReceive {
    @Override
  public String getInfo(){
        return "郵箱訊息:Hello, World!";
  }
}

/**
 * 微笑訊息類
  */ class Weixin implements IReceive {
    @Override
  public String getInfo() {
        return "微信訊息:Are you OK";
  }
}

class Person1{
    public void receive(IReceive receive){
        System.out.println(receive.getInfo());
  }
}

依賴倒轉原則關係傳遞的三種方式

  1. 構造器傳遞
  2. setter方法傳遞
  3. 方法介面傳遞

程式碼示例

/**
 * @author HiWin10
 * @date 2020/7/31
 * 依賴倒轉原則三種傳遞方式
  */ public class DependencyPass {
}

// 方式1:通過介面傳遞 // 開關的介面 interface IOpenAndClose {
    public void open(ITV itv); // 抽象方法,接受介面 }

/**
 * 介面
  */ interface ITV {
    public void play(); }

/**
 * 電視的實現
  */ class ChangHong implements ITV {
    @Override
  public void play() {
        System.out.println("長虹電視開機了...");
  }
}

/**
 * 操作類的實現
  */ class OpenAndClose implements IOpenAndClose {
    @Override
  public void open(ITV itv) {
        itv.play();
  }
}

/*******************/ // 方式2:構造器傳遞 interface IOpenAndClose2 {
    public void open(ITV itv); // 抽象方法,接受介面 }
/**
 * 介面
  */ interface ITV2 {
    public void play(); }

class ChangHong2 implements ITV2 {
    @Override
  public void play() {
        System.out.println("長虹電視開機了...");
  }
}

/**
 * 構造器方式
  */ class OpenAndClose2 implements IOpenAndClose2 {
    private ITV2 itv2;   public OpenAndClose2(ITV2 itv2) {
        this.itv2 = itv2;
  }

    @Override
  public void open(ITV itv) {

    }
}

/*******************/ // 方式3:set方法 interface IOpenAndClose3 {
    public void open(ITV itv); // 抽象方法,接受介面 }
/**
 * 介面
  */ interface ITV3 {
    public void play(); }

class ChangHong3 implements ITV3 {
    @Override
  public void play() {
        System.out.println("長虹電視開機了...");
  }
}

/**
 * set方法
  */ class OpenAndClose3 implements IOpenAndClose3 {
    private ITV3 itv3;   public void setItv3(ITV3 itv3) {
        this.itv3 = itv3;
  }

    @Override
  public void open(ITV itv) {
        this.itv3.play();
  }
}

小結

  1. 底層模組儘量都要有抽象或介面。或者兩者都有,程式穩定性會更好
  2. 變數的宣告型別儘量是抽象類或介面,這樣我們的變數引用和實際物件間,就存在一個緩衝層,利於程式擴充套件和優化
  3. 繼承時遵循里氏替換原則

里氏替換原則

繼承帶來的優劣勢

  1. 繼承包含這樣一個含義:父類中凡是已經實現好的方法,實際上是在設定規範和契約,雖然沒有強制所有子類必須遵循這些契約,但是如果子類隨意重寫父類實現的方法,就會對繼承體系造成破壞

  2. 繼承在給程式帶來便利同時,也會有弊端。比如使用繼承會給程式帶來侵入性,程式的可移植性減低,增加物件的耦合,如果一個類被其他類所繼承,則這個類需要修改時,必須考慮所有子類,並且父類修改後,所有涉及到子類的功能都有可能產生故障

  3. 問題提示:在程式設計中,如何正確的使用繼承?=> 里氏替換原則

里氏替換原則概念

  1. 如果對每個型別為T1的物件o1,都有型別為T2的物件o2,使得以T1定義的所有程式P在所有物件o1都代還成o2時,程式P的行為沒有發生變化,那麼T2是型別T1的子類。或句話說,所有引用基類的地方必須能透明地使用其子類物件
  2. 使用繼承時,遵循里氏替換原則,在子類中儘量不要重寫父類的方法
  3. 里氏替換原則告訴我們,繼承實際上讓兩個類的耦合提高了,在適當情況下,可以通過聚合、組合、依賴來解決問題

問題產出

/**
 * @author HiWin10
 * @date 2020/8/3
 * 里氏替換原則
  * 里氏替換原則需要滿足一下幾點:
  *      1. 所有使用基類的地方都可以使用子類來代替
  *      2. 儘量不要重寫父類的方法
  *      3. 在適當情況下,可以選擇使用聚合、組合、依賴來解決問題
  */ public class Liskow1 {
    public static void main(String[] args) {
        A a = new A();
  System.out.println("20-10=" + a.func(20, 10));
  System.out.println("20-18=" + a.func(20, 18));    System.out.println("------------");
  B b = new B();
  System.out.println("11-3=" + b.func(10, 20));
  System.out.println("20-18+1=" + b.fun2(20, 18));    }
}

class A {
    /**
 * 減法
  */
  public Integer func(int a, int b){
        return a - b;
  }
}

// 按照main中B物件的邏輯,是想要使用A的方法,但是B類不小心重寫了A的其中方法,所以說才會發生此事,計算失誤。違反了里氏替換原則 // 解決辦法:使用聚合、組合、依賴來解決問題 class B extends A {
    /**
 * B類不小心重寫了 A類的方法
  */
  public Integer func(int a, int b) {
        return a + b;
  }

    public Integer fun2(int a, int b) {
        return func(a, b) + 1;
  }
}
  • A類有一個方法func作用減法。
  • B類繼承A類想要在main中使用func但是不小心重寫了A類的func方法,所以在使用的時候邏輯上不正確
  • 解決方法就是使用使用聚合、組合、依賴來解決問題

程式碼如下:

/**
 * @author HiWin10
 * @date 2020/8/3
 * 里氏替換原則
  * 里氏替換原則需要滿足一下幾點:
  *      1. 所有使用基類的地方都可以使用子類來代替
  *      2. 儘量不要重寫父類的方法
  *      3. 在適當情況下,可以選擇使用聚合、組合、依賴來解決問題
  */ public class Liskow2 {
    public static void main(String[] args) {
        A1 a = new A1();
  System.out.println("20-10=" + a.func(20, 10));
  System.out.println("20-18=" + a.func(20, 18));    System.out.println("------------");
  B1 b = new B1();
  System.out.println("11-3=" + b.func(11, 3));
  System.out.println("20-18+1=" + b.fun2(20, 18));    }
}

class A1 {
    /**
 * 減法
  */
  public Integer func(int a, int b){
        return a - b;
  }
}

// 按照main中B物件的邏輯,是想要使用A的方法,但是B類不小心重寫了A的其中方法,所以說才會發生此事,計算失誤。違反了里氏替換原則 // 解決辦法:使用聚合、組合、依賴來解決問題/本次使用組合來解決 class B1  {
    private A1 a = new A1();    /**
 * B類不小心重寫了 A類的方法
  */
  public Integer func(int a, int b) {
//        return a + b;
  return this.a.func(a, b);
  }

    public Integer fun2(int a, int b) {
        return this.a.func(a, b) + 1;
  }
}

開閉原則[最重要]

概念

  1. 開閉原則是程式設計中最基礎、最重要的原則
  2. 一個軟體實體如類,模組和函式應該對擴充套件開發(對提供方),對修改關閉(對使用方)。由抽象構建框架,用實現擴充套件細節。
  3. 當軟體需要變化時,儘量通過擴充套件軟體實體的行為來實現變化,而不是通過修改已有程式碼實現變化。
  4. 程式設計中遵循其他原則,以及使用設計模式的目的就是遵循開閉原則

程式碼示例
該程式碼是,通過方法來決定繪畫那種圖形,這裡是沒有遵循開閉原則的,你會發現,再新增一個圖形的時候,不管是服務方還是使用方都要修改或擴充套件程式碼

/**
 * @author HiWin10
 * @date 2020/8/3
 * 開閉原則
  *  1. 對功能擴充套件開放,對修改關閉
  *  2. 儘量使用軟體的實體行為實現變化,而不是修改已有程式碼
  */ public class Ocp1 {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
  graphicEditor.drawShape(new Rectangle());
  graphicEditor.drawShape(new Circle());    // 繪畫新新增的三角形
  graphicEditor.drawShape(new Triangle());
  }
}

// 沒有遵循開閉原則,當要新增一個圖形的時候,要修改的東西太多, // 首先,服務需要修改 其次使用方也要修改程式碼 // [使用方] // 解決辦法:新增抽象層,新增好規範好,由具體實現來完成具體圖形的繪畫 
class GraphicEditor {
  public void drawShape(Shape s){
        if (s.m_type == 1){
            drawRectangle(s);
  } else  if (s.m_type == 2) {
            drawCircle(s);
  } // 首先這裡修改 新增的三角形
  else if (s.m_type == 3){
            drawTriangle(s);
  }
    }

    // 繪製矩形
  private void drawRectangle(Shape s) {
        System.out.println(" 繪製矩形 ");
  }
    // 繪製圓形
  private void drawCircle(Shape s) {
        System.out.println(" 繪製圓型 ");
  }

    // 這裡修改新增新方法drawTriangle畫三角形
  private void drawTriangle(Shape s){
        System.out.println("繪製 三角形");
  }
}

class Shape {
    int m_type; }

// 矩形 class Rectangle extends Shape {
    public Rectangle() {
        super.m_type = 1;
  }
}

// 圓形 class Circle extends Shape {
    public Circle() {
        super.m_type = 2;
  }
}

// 新新增三角形 [服務方] class Triangle extends Shape {
    public Triangle() {
        super.m_type = 3;
  }
}

問題

  • 雖然對提供方是擴充套件(新增類),對使用方還是修改
  • 解決思路:中間新增抽象層,然後圖形的繪畫交給具體實現,而不是由使用方來繪畫
/**
 * @author HiWin10
 * @date 2020/8/3
 * 開閉原則
  *  1. 對功能擴充套件開放,對修改關閉
  *  2. 儘量使用軟體的實體行為實現變化,而不是修改已有程式碼
  *
 * 開始使用開閉原則
  * 新增抽象類,定義介面 然後每個實現來繪畫物件圖形
  */ public class Ocp2 {
    public static void main(String[] args) {
        GraphicEditor1 graphicEditor = new GraphicEditor1();
  graphicEditor.drawShape(new Rectangle1());
  graphicEditor.drawShape(new Circle1());    // 繪畫新新增的三角形
  graphicEditor.drawShape(new Triangle1());
  }
}

class GraphicEditor1 {
    public void drawShape(Shape2 s){
        s.drawShape();
  }
}

abstract class Shape2 {
    int m_type;
 public abstract void drawShape(); }

// 矩形 class Rectangle1 extends Shape2 {
    public Rectangle1() {
        super.m_type = 1;
  }

    @Override
  public void drawShape() {
        System.out.println(" 繪製矩形 ");
  }
}

// 圓形 class Circle1 extends Shape2 {
    public Circle1() {
        super.m_type = 2;
  }

    @Override
  public void drawShape() {
        System.out.println(" 繪製圓型 ");
  }
}

// 新新增三角形 class Triangle1 extends Shape2 {
    public Triangle1() {
        super.m_type = 3;
  }

    @Override
  public void drawShape() {
        System.out.println("繪製 三角形");
  }
}

小結
以後在設計程式儘量使用抽象,這樣的話程式會非常靈活。但是同樣的程式也會非常負責。

迪米特法則

概念

  1. 一個物件應該對其他物件保持最少了解
  2. 類與類的關係越密切,耦合度越高
  3. 迪米特法則,又叫最少知道原則。既一個類對自己依賴的類知道的越少越好。也就是說。對於依賴的類不管多麼複雜,都要儘量將邏輯封裝在類的內部,對外除了提供的public方法,不對外洩露任何資訊
  4. 迪米特法則還有更簡單的定義:只與直接的朋友通訊
  5. 直接朋友:每個物件都會與其他物件有耦合關係,只要連個物件之間有耦合關係,我們就說這兩個物件是朋友關係。我們稱出現成員變數、方法引數、方法返回值就是直接朋友,而在區域性出現的類就不是直接朋友。也就是說,陌生的類最好不要以區域性變數方式存在類中

耦合關係分別有:依賴,關聯,組合,聚合等。

程式碼示例

  1. 有一個學校,下屬有各個學院和總部,現要求打印出學校總部員工ID和學院員工ID
  2. 程式碼演示
/**
 * @author HiWin10
 * @date 2020/8/4
 * 迪米特法則
  * 1. 一個物件應該對其他物件保持最少的瞭解
  * 2. 類與類關係密切,耦合度就越高
  * 3. 迪米特法則又叫最少知道原則,既一個類對自己依賴的類知道越少越好,也就是說,對被依賴的類不管多麼複雜,都要儘量將邏輯封裝在類內部,對外提供public方法,不對外洩露
  * 4. 迪米特法則還有更簡單定義:只與直接朋友通訊
  *
 * 直接朋友:該類的屬性,或者setter方法;或者方法引數
  */ public class Demeter1 {
    public static void main(String[] args) {
        SchoolManager schoolManager = new SchoolManager();
  schoolManager.printAllEmployee(new CollegeManager());
  }
}

// 學院總員工類 class Employee {
    private String id;   public String getId() {
        return id;
  }

    public void setId(String id) {
        this.id = id;
  }

    @Override
  public String toString() {
        return "Employee{" +
                "id='" + id + '\'' +
                '}';
  }
}

// 學院的員工類 class CollegeEmployee {
    private String id;   public String getId() {
        return id;
  }

    public void setId(String id) {
        this.id = id;
  }

    @Override
  public String toString() {
        return "CollegeEmployee{" +
                "id='" + id + '\'' +
                '}';
  }
}

// 管理學院員工的管理類 class CollegeManager {
    // 返回學院所有員工
  public List<CollegeEmployee> getAllEmployee() {
        List<CollegeEmployee> list = new ArrayList<>();
 for (int i = 0; i < 10; i++) {
            CollegeEmployee emp = new CollegeEmployee();
  emp.setId("學院員工id = " + i);
  list.add(emp);
  }
        return list;
  }
}

// 學院管理類 // 分析:該類的直接朋友:Employee、CollegeManager // CollegeEmployee 類不是直接朋友違背了迪米特法則。將這一段程式碼移動到該類實現上去 class SchoolManager {
    // 返回學院總部的員工
  public List<Employee> getAllEmployee() {
        List<Employee> list = new ArrayList<>();
 for (int i = 0; i < 5; i++) {
            Employee emp = new Employee();
  emp.setId("學院總員工ID = " + i);
  list.add(emp);
  }
        return list;
  }

    // 該方法返回輸出學院總部和學院員工資訊
  public void printAllEmployee(CollegeManager sub) {
        // 獲取到學院員工
  // 解決辦法:將這一段程式碼移動到該類實現上去,
  List<CollegeEmployee> list = sub.getAllEmployee();
  System.out.println("-------學院員工-------");
  list.forEach(System.out::println);    // 獲取學院總部員工
  List<Employee> employeeList = this.getAllEmployee();
  System.out.println("-------學院總員工-------");
  employeeList.forEach(System.out::println);
  }
}

問題發現

  1. CollegeEmployee 類不是直接朋友違背了迪米特法則。將這一段程式碼移動到該類實現上
/**
 * @author HiWin10
 * @date 2020/8/4
 * 迪米特法則
  * 1. 一個物件應該對其他物件保持最少的瞭解
  * 2. 類與類關係密切,耦合度就越高
  * 3. 迪米特法則又叫最少知道原則,既一個類對自己依賴的類知道越少越好,也就是說,對被依賴的類不管多麼複雜,都要儘量將邏輯封裝在類內部,對外提供public方法,不對外洩露
  * 4. 迪米特法則還有更簡單定義:只與直接朋友通訊
  *
 * 直接朋友:該類的屬性,或者setter方法;或者方法引數
  */ public class Demeter2 {
    public static void main(String[] args) {
        SchoolManager2 schoolManager = new SchoolManager2();
  schoolManager.printAllEmployee(new CollegeManager2());
  }
}

// 學院總員工類 class Employee2 {
    private String id;   public String getId() {
        return id;
  }

    public void setId(String id) {
        this.id = id;
  }

    @Override
  public String toString() {
        return "Employee{" +
                "id='" + id + '\'' +
                '}';
  }
}

// 學院的員工類 class CollegeEmployee2 {
    private String id;   public String getId() {
        return id;
  }

    public void setId(String id) {
        this.id = id;
  }

    @Override
  public String toString() {
        return "CollegeEmployee{" +
                "id='" + id + '\'' +
                '}';
  }
}

// 管理學院員工的管理類 class CollegeManager2 {
    // 返回學院所有員工
  public List<CollegeEmployee2> getAllEmployee() {
        List<CollegeEmployee2> list = new ArrayList<>();
 for (int i = 0; i < 10; i++) {
            CollegeEmployee2 emp = new CollegeEmployee2();
  emp.setId("學院員工id = " + i);
  list.add(emp);
  }
        return list;
  }

    // 顯示移動到改方法中
  public void printAllemployee() {
        List<CollegeEmployee2> list = this.getAllEmployee();
  System.out.println("-------學院員工-------");
  list.forEach(System.out::println);
  }
}

// 學院管理類 // 分析:該類的直接朋友:Employee、CollegeManager // CollegeEmployee 類不是直接朋友違背了迪米特法則。將這一段程式碼移動到該類實現上去 class SchoolManager2 {
    // 返回學院總部的員工
  public List<Employee2> getAllEmployee() {
        List<Employee2> list = new ArrayList<>();
 for (int i = 0; i < 5; i++) {
            Employee2 emp = new Employee2();
  emp.setId("學院總員工ID = " + i);
  list.add(emp);
  }
        return list;
  }

    // 該方法返回輸出學院總部和學院員工資訊
  public void printAllEmployee(CollegeManager2 sub) {
        // 獲取到學院員工
  // 解決辦法:將這一段程式碼移動到該類實現上去,
  sub.printAllemployee();    // 獲取學院總部員工
  List<Employee2> employeeList = this.getAllEmployee();
  System.out.println("-------學院總員工-------");
  employeeList.forEach(System.out::println);
  }
}

合成複用原則

就是儘量以合成/聚合的方式,而不是使用繼承

設計模式總結

  1. 找到應用中可能要變化之處,把他們獨立出來,不要和那些不需要變化的程式碼混在一起
  2. 針對介面程式設計。而不是針對實現程式設計
  3. 為了互動物件之間鬆耦合設計而努力