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

設計模式--7大原則

問題:編寫程式碼過程中,程式設計師面臨著來自耦合性,內聚性以及可維護性,可擴充套件性,重用性,靈活性等挑戰.

設計模式的好處

1、提到程式碼衝用心(,相同程式碼,不用多次編寫)

2、可讀性(,程式設計規範性,便於其他程式設計師的閱讀和理解)

3、可擴充套件性(,當需要增加新的功能時,非常的方便,成為可維護性)

4、可靠性(,當我們增加新的功能後,對原來的功能沒有影響)

5、使程式高內聚、低耦合

設計模式原則,其實就是程式設計師在程式設計時,應當遵守的原則,也是各種設計模式的基礎(,設計模式為什麼這樣設計的依據)

一、單一職責原則

基本介紹

對類來說,即一個類應該只負責一項職責(不是一個方法

).如類A負責兩個不同職責:職責1,職責2.

當職責1需求變更而改變A,可能造成職責2執行錯誤,所以需要將類A的粒度分解為A1,A2.

應用例項

做專案時的單一職責,userDao,只做user的資料庫相關功能.

程式碼:

錯誤示範

public class SingleResponsibility1 {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("摩托車");
        vehicle.run("汽車");
        vehicle.run(
"飛機"); } } /** * 交通工具類 * 1、在方式1的run方法中,違反了單一職責原則 * 2、解決的方案非常簡單,根據交通工具執行方法不同,分解為不同的類即可 */ class Vehicle { public void run(String vehicle) { System.out.println(vehicle + "running on the load..."); } }

改進

public class SingleResponsibility2 {
    public static void main(String[] args) {
        RoadVehicle roadVehicle 
= new RoadVehicle(); roadVehicle.run("摩托車"); roadVehicle.run("汽車"); AirVehicle airVehicle = new AirVehicle(); airVehicle.run("飛機"); } } /** * 方案2分析 * 1、遵守單一職責原則 * 2、但是這樣做改動很大,即將類分解,同時修改客戶端 * 3、改進:直接修改Vehicle 類,改動的程式碼會比較少=》方案3 */ class RoadVehicle { public void run(String vehicle) { System.out.println(vehicle + " running on the load..."); } } class AirVehicle { public void run(String vehicle) { System.out.println(vehicle + " flying in the sky..."); } } class WaterVehicle { public void run(String vehicle) { System.out.println(vehicle + " running in the water..."); } }

改進

public class SingleResponsibility3 {
    public static void main(String[] args) {
        Vehicle2 vehicle2 = new Vehicle2();
        vehicle2.run("汽車");
        vehicle2.runWater("輪船");
        vehicle2.runAir("飛機");
    }
}


/**
 * 方式3分析
 * 1、這種修改方法沒有對原來的類做大的修改,只是增加方法
 * 2、這裡雖然沒有在類這個級別上遵守單一職責原則,但是在方法級別上,仍然是遵守單一職責原則
 */
class Vehicle2 {
    public void run(String vehicle) {
        System.out.println(vehicle + " running on the load...");
    }

    public void runAir(String vehicle) {
        System.out.println(vehicle + " flying in the sky...");
    }

    public void runWater(String vehicle) {
        System.out.println(vehicle + " running in the water...");
    }
}

小結

單一職責原則注意事項和細節

  1、降低類的複雜度,一個類只負責一項職責

  2、提高類的可讀性,可維護性

  3、降低變更引起的風險

  4、通常情況下,我們應當遵守單一職責原則,只有邏輯足夠簡單,才可以在程式碼級違反單一職責原則;只有類中方法數量足夠多,可以在方法級別保持單一職責原則

    a):車輛的出行方式,如果只有很少的出行方式,那麼只需要幾個方法就夠了(例如:電動車、自動車、小汽車),但是如果有很多出行方式,那麼最好拆分為多個類(例如:路上跑的、水上跑的,天上飛的),每個類負責不同型別車輛的出行方式

單一職責原則的一點思考,什麼時候拆分為多個類?

當只需要幾個方法就需要完成,類的方法邏輯簡單時,可以只用一個類(多個類開銷大),但是如果有很多方法,儘量拆分為多個類,每個類負責一批方法.

二、介面隔離原則(interface Segregation Principle)

基本介紹

1、客戶端不應該依賴它不需要的介面,即一個類的依賴應該建立在最小的介面上

依賴介面時,希望我們所依賴的介面是最小的,用不到的方法應該進行隔離,隔離所用的方法就是介面隔離原則把介面進行拆分

2、如圖所示

3、A通過介面Interface依賴類B,C通過介面Interface1依賴類D,如果介面Interface1對於Interface1對於類A和類C來說不是最小介面,那麼類B和類D必須去實現他們不需要的方法

4、介面隔離原則應該這樣處理

  a)將介面Interface1拆分為獨立的幾個介面(這裡我們拆分為3個介面),A和類C分別與他們需要的介面建立依賴關係.也就是介面隔離原則.

應用例項

1、A通過介面Interface1依賴類B,C通過介面Interface1依賴類D,請編寫程式碼完成此應用例項

錯誤示範:

interface Interface1 {
    void operation1();

    void operation2();

    void operation3();

    void operation4();

    void operation5();
}


class B implements Interface1 {
    @Override
    public void operation1() {
        System.out.println("B 實現了 operation1");
    }

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

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

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

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

class D implements Interface1 {
    @Override
    public void operation1() {
        System.out.println("D 實現了 operation1");
    }

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

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

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

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

/**
 * C類通過介面Interface1依賴(使用)D類,但是隻會用到1,4,5方法
 */
class C {
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend4(Interface1 i) {
        i.operation4();
    }

    public void depend5(Interface1 i) {
        i.operation5();
    }
}

應傳統方法的問題和使用介面隔離原則進行改進

1、A通過介面Interface依賴類B,C通過Interface1依賴類D,如果介面Interface1對於類A和類C來說不是最小介面,那麼類B和類D必須去實現他們不需要的方法

2、將介面Interface1拆分為獨立的幾個介面,A和類C分別與他們需要的介面建立依賴關係.也就是採用介面隔離原則

3、介面Interface1出現的方法,根據實際情況拆分為3個介面

程式碼改進

public class Segregation1 {
    public static void main(String[] args) {

    }
}

/**
 * 介面1
 */
interface Interface1 {
    void operation1();
}

/**
 * 介面2
 */
interface Interface2 {
    void operation2();

    void operation3();
}

/**
 * 介面3
 */
interface Interface3 {
    void operation4();

    void operation5();
}

class B implements Interface1, Interface2 {

    @Override
    public void operation1() {
        System.out.println("B 實現了 operation1");
    }

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

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

class D implements Interface1, Interface3 {

    @Override
    public void operation1() {
        System.out.println("D 實現了 operation1");
    }

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

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

/**
 * A類通過介面Interface1,INterface2依賴(使用)B類,但是隻會用到1,2,3方法
 */
class A {
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend2(Interface2 i) {
        i.operation2();
    }

    public void depend3(Interface2 i) {
        i.operation3();
    }

}

/**
 * C類通過介面Interface1,Interface3依賴(使用)D類,但是隻會用到1,4,5方法
 */
class C{
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend2(Interface3 i) {
        i.operation4();
    }

    public void depend3(Interface3 i) {
        i.operation5();
    }
    
}

三、依賴倒轉原則

基本介紹

依賴倒轉原則是指

1、高層模組不應該依賴底層模組,二者都應該依賴其抽象

2、抽象不應該依賴細節,細節應該依賴抽象

3、依賴倒轉(倒置)的中心思想是面向介面程式設計

4、依賴倒轉原則是基於這樣的設計理念:相對於細節的多變性,抽象的東西要穩定的多.以抽象為基礎搭建的架構比細節為基礎的架構要穩定的多.java,抽象指的是介面或抽象類,細節就是具體的實現類

5、使用介面或抽象類的目的是制訂好規範,而不涉及任何具體的操作,把展現細節的任務交給他們的實現類去完成

應用例項

請完成Person接收訊息的功能

1、實現方案1

public class DependencyInversion {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
    }


}

class Email {
    public String getInfo() {
        return "電子郵件資訊:helloword";
    }
}

/**
 * 完成Person接收訊息的功能
 * 方式1分析
 * 1、簡單,比較容易想到
 * 2、如果我們獲取的物件是微信,簡訊等,則新增類,同時Persons也要增加相應接受方法
 * 3、解決思路:引入一個抽象的介面,IReceiver,表示接受者,這樣Person與介面IReceiver發生依賴
 * 因為Email,WeChat等等屬於接收的範圍,他們各自實現IReceiver介面就ok,這樣我們就符合依賴倒轉原則
 */
class Person {
    public void receive(Email email) {
        System.out.println(email.getInfo());
    }
}

改進

public class DependencyInversion {
    public static void main(String[] args) {
        //客戶端無需改變
//細節依賴於抽象
        Person person = new Person();
        person.receive(new Email());
        person.receive(new WeChat());
    }
}

interface IReceiver {
    public String getInfo();
}

class Email implements IReceiver {
    @Override
    public String getInfo() {
        return "電子郵件資訊:hello world";
    }
}

class WeChat implements IReceiver {
    @Override
    public String getInfo() {
        return "微信資訊:hello weChat";
    }
}

class Person {
    public void receive(IReceiver receiver) {
    //抽象不依賴於細節
        System.out.println(receiver.getInfo());
    }
}

依賴關係傳遞的三種方式和應用案例

1、介面傳遞

2、構造方法傳遞

3、setter方法傳遞

應用案例程式碼

介面傳遞

/**
 * 方式1:通過介面傳遞實現依賴
 * 開關介面
 */
interface IOpenAndClose{
    /**
     * 抽象方法,接收介面
     * @param itv
     */
    public void open(ITV itv);
}

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

class ChangHong implements ITV{

    @Override
    public void ploy() {
        System.out.println("開啟長虹電視。。。");
    }
}

/**
 * 實現介面:通過介面傳遞
 */
class OpenAndClose implements IOpenAndClose{

    @Override
    public void open(ITV tv) {
        tv.ploy();
    }
}

構造器傳遞

/**
 * 方式2
 * 構造器傳遞
 */
interface IOpenAndClose {
    /**
     * 抽象方法,接收介面
     */
    public void open();
}

interface ITV {
    public void ploy();
}


class OpenAndClose implements IOpenAndClose {
    ITV tv;

    OpenAndClose(ITV tv){
        this.tv = tv;
    }

    @Override
    public void open() {
        tv.ploy();
    }
}

setter方法傳遞

/**
 * 放鬆3:setter方法注入
 */
interface IOpenAndClose{
    public void open();

    public void setTv(ITV itv);
}

interface ITV{
    public void ploy();
}

class OpenAndClose implements IOpenAndClose{
    ITV tv;

    @Override
    public void open() {
        tv.ploy();
    }

    @Override
    public void setTv(ITV itv) {
        this.tv = itv;
    }
}

其實這三種傳遞方式有一個共性,就是ITVIOpenAndCloseOpenAndClose三者承擔起了整個架構,基本不變,其他具體tv只要實現ITV介面,就可以保持依賴倒置原則.

依賴倒轉原則的注意事項和細節

1、底層模組儘量都要有抽象類或介面,或者兩者都有,程式穩定性更好

2、變數的生命型別儘量是介面或抽象型別,這樣我們的變數引用和實際物件間,就存在一個緩衝層,利於程式擴充套件和優化

3、繼承時遵循里氏替換原則

四、里氏替換原則