自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

大白話式聊聊設計模式中的開閉原則

開發(fā) 前端
在開閉原則中,抽象化是一個關鍵,解決問題的關鍵在于抽象化,在 JAVA 語言這種面向?qū)ο蟮恼Z言中,可以給系統(tǒng)定義出一個一勞永逸的,不再更改的抽象化的設計,此設計允許擁有無窮無盡的實現(xiàn)層被實現(xiàn)。

一、前言

在面向?qū)ο蟮能浖O計中,只有盡量降低各個模塊之間的耦合度,才能提高代碼的復用率,系統(tǒng)的可維護性、可擴展性才能提高。面向?qū)ο蟮能浖O計中,有23種經(jīng)典的設計模式,是一套前人代碼設計經(jīng)驗的總結(jié),如果把設計模式比作武功招式,那么設計原則就好比是內(nèi)功心法。常用的設計原則有七個,具體如下:

  • 單一職責原則:專注降低類的復雜度,實現(xiàn)類要職責單一;
  • 開放關閉原則:所有面向?qū)ο笤瓌t的核心,設計要對擴展開發(fā),對修改關閉;
  • 里式替換原則:實現(xiàn)開放關閉原則的重要方式之一,設計不要破壞繼承關系;
  • 依賴倒置原則:系統(tǒng)抽象化的具體實現(xiàn),要求面向接口編程,是面向?qū)ο笤O計的主要實現(xiàn)機制之一;
  • 接口隔離原則:要求接口的方法盡量少,接口盡量細化;
  • 迪米特法則:降低系統(tǒng)的耦合度,使一個模塊的修改盡量少的影響其他模塊,擴展會相對容易;
  • 組合復用原則:在軟件設計中,盡量使用組合/聚合而不是繼承達到代碼復用的目的。

這些設計原則并不說我們一定要遵循他們來進行設計,而是根據(jù)我們的實際情況去怎么去選擇使用他們,來讓我們的程序做的更加的完善。

今天我們就來聊聊其中的單一職責原則和開閉原則實現(xiàn)理念,歡迎大家在評論區(qū)留言,一起探討。

二、單一職責原則

單一職責原則,簡單的說:就一個類而言,應該僅有一個引起它變化的原因,通俗的說,就是一個類只負責一項職責。

此原則的核心就是解耦和增強內(nèi)聚性

那么為什么要使用單一職責原則:

如果一個類承擔的職責過多,就等于把這些職責耦合在一起,一個職責的變化可能會削弱或者抑制這個類完成其他職責的能力。這種耦合會導致脆弱的設計。

這也是他的優(yōu)點,我們總結(jié)一下:

  • 降低類的復雜度;
  • 提高類的可讀性,提高系統(tǒng)的可維護性;
  • 降低變更引起的風險(降低對其他功能的影響)。

我們來舉一些簡單的例子來說明一下這個單一職責原則

//我們用動物生活來做測試
class Animal{
    public void breathe(String animal){
        System.out.println(animal+"生活在陸地上");
    }
}
public class Client{
    public static void main(String[] args){
        Animal animal = new Animal();
        animal.breathe("羊");
        animal.breathe("牛");
        animal.breathe("豬");
    }
}

運行結(jié)果:

羊生活在陸地上
牛生活在陸地上
豬生活在陸地上

但是問題來了,動物并不是都生活在陸地上的,魚就是生活在水中的,修改時如果遵循單一職責原則,需要將 Animal 類細分為陸生動物類 Terrestrial,水生動物 Aquatic,代碼如下:

class Terrestrial{
    public void breathe(String animal){
        System.out.println(animal+"生活在陸地上");
    }
}
class Aquatic{
    public void breathe(String animal){
        System.out.println(animal+"生活在水里");
    }
}

public class Client{
    public static void main(String[] args){
        Terrestrial terrestrial = new Terrestrial();
        terrestrial.breathe("羊");
        terrestrial.breathe("牛");
        terrestrial.breathe("豬");

        Aquatic aquatic = new Aquatic();
        aquatic.breathe("魚");
    }
}

運行結(jié)果:

羊生活在陸地上
牛生活在陸地上
豬生活在陸地上
魚生活在水里

但是問題來了如果這樣修改花銷是很大的,除了將原來的類分解之外,還需要修改客戶端。而直接修改類 Animal 來達成目的雖然違背了單一職責原則,但花銷卻小的多,代碼如下:

class Animal{
    public void breathe(String animal){
        if("魚".equals(animal)){
            System.out.println(animal+"生活在水中");
        }else{
            System.out.println(animal+"生活在陸地上");
        }
    }
}

public class Client{
    public static void main(String[] args){
        Animal animal = new Animal();
        animal.breathe("羊");
        animal.breathe("牛");
        animal.breathe("豬");
        animal.breathe("魚");
    }
}

可以看到,這種修改方式要簡單的多。但是卻存在著隱患:有一天需要將魚分為生活在淡水中的魚和生活在海水的魚,則又需要修改 Animal 類的 breathe 方法,而對原有代碼的修改會對調(diào)用“豬”“?!薄把颉钡认嚓P功能帶來風險,也許某一天你會發(fā)現(xiàn)程序運行的結(jié)果變?yōu)椤芭I钤谒小绷恕?/p>

這種修改方式直接在代碼級別上違背了單一職責原則,雖然修改起來最簡單,但隱患卻是最大的。還有一種修改方式:

class Animal{
    public void breathe(String animal){
        System.out.println(animal+"生活在陸地上");
    }

    public void breathe2(String animal){
        System.out.println(animal+"生活在水中");
    }
}

public class Client{
    public static void main(String[] args){
        Animal animal = new Animal();
        animal.breathe("牛");
        animal.breathe("羊");
        animal.breathe("豬");
        animal.breathe2("魚");
    }
}

可以看到,這種修改方式?jīng)]有改動原來的方法,而是在類中新加了一個方法,這樣雖然也違背了單一職責原則,但在方法級別上卻是符合單一職責原則的,因為它并沒有動原來方法的代碼。

這三種方式各有優(yōu)缺點,那么在實際編程中,采用哪一中呢?其實這真的比較難說,需要根據(jù)實際情況來確定。我的原則是:只有邏輯足夠簡單,才可以在代碼級別上違反單一職責原則;只有類中方法數(shù)量足夠少,才可以在方法級別上違反單一職責原則;

例如本文所舉的這個例子,它太簡單了,它只有一個方法,所以,無論是在代碼級別上違反單一職責原則,還是在方法級別上違反,都不會造成太大的影響。實際應用中的類都要復雜的多,一旦發(fā)生職責擴散而需要修改類時,除非這個類本身非常簡單,否則還是遵循單一職責原則的好。

以上就是我所說的單一職責原則了,很多書中介紹的說它并不屬于面向?qū)ο笤O計原則中的一種,但是我認為它是,所以我就把他解釋出來了。

三、開放關閉原則

開放關閉原則又稱為開放封閉原則。

簡單的說:一個軟件實體應該對擴展開放,對修改關閉,這個原則也是說,在設計一個模塊的時候,應當使這個模塊可以在不被修改的前提下被擴展,換句話說,應當可以在不必修改源碼的情況下改變這個模塊的行為。

這句話其實剛開始看上去是有些矛盾的,接下來在我后邊文章解釋里面,我會把他解釋清楚一點。

我們先用個比較好玩的例子來說一下。

西游記大家都看過,玉帝招安孫悟空的時候的橋段,大家還有沒有印象?

當年大鬧天宮的時候美猴王對玉皇大帝做了個挑戰(zhàn),美猴王說:皇帝輪流做,明年到我家,只叫他搬出去,將天宮讓給我,對于這個挑戰(zhàn),太白金星給玉皇大帝提了個意見,我們把它招上來,給他個小官做,他不就不鬧事了?

換一句話說,不用興師動眾的,不破壞天庭的規(guī)矩這就是閉,但是收他為官,便是開,招安的方法便是符合開閉原則的,給他個‘弼馬溫’的官,便可以讓這個系統(tǒng)正常不受威脅,是吧,何樂而不為?我給大家畫個圖。

圖片圖片

招安的方法的關鍵就是不允許更改現(xiàn)有的天庭秩序,但是允許將妖猴納入現(xiàn)有的秩序中,從而擴展了這個秩序,用面向?qū)ο蟮恼Z言來說:不允許更改的是系統(tǒng)的抽象層,而允許擴展的是系統(tǒng)的實現(xiàn)層。

我們寫一些簡單的代碼來進行一個完整的展示來進行一下理解。

//書店賣書
interface Books{
    //書籍名稱
    public Sting getName();
    //書籍價格
    public int getPrice();
    //書籍作者
    public String getAuthor();
}
    
public class NovelBook implements Books {
    private String name;

    private int price;

    private String author;

    public NovelBook(String name, int price, String author) {
        this.name = name;
        this.price = price;
        this.author = author;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getPrice() {
        return price;
    }

    @Override
    public String getAuthor() {
        return author;
    }
}

以上的代碼是數(shù)據(jù)的實現(xiàn)類和書籍的類別;

下面我們將要開始對書籍進行一個售賣活動:

public class BookStore {
    private final static ArrayList<Books> sBookList = new ArrayList<Books>();

    static {
        sBookList.add(new NovelBook("天龍八部", 4400, "金庸"));
        sBookList.add(new NovelBook("射雕英雄傳", 7600, "金庸"));
        sBookList.add(new NovelBook("鋼鐵是怎么煉成的", 7500, "保爾·柯查金"));
        sBookList.add(new NovelBook("紅樓夢", 3300, "曹雪芹"));
    }

    public static void main(String[] args) throws IOException {
        NumberFormat format = NumberFormat.getCurrencyInstance();
        format.setMaximumFractionDigits(2);
       System.out.println("----書店賣出去的書籍記錄如下---");
        for (Books book : sBookList) {
            System.out.println("書籍名稱:" + book.getName()
                    + "\t書籍作者:" + book.getAuthor()
                    + "\t書籍價格:" + format.format(book.getPrice() / 100.00) + "元");
        }
    }
}

運行結(jié)果如下:

D:\develop\JDK8\jdk1.8.0_181\bin\java.exe "-javaagent:D:\develop\IDEA\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar=62787:D:\develop\IDEA\IntelliJ IDEA 2018.2.4\bin" -Dfile.encoding=UTF-8 -classpath D:\develop\JDK8\jdk1.8.0_181\jre\lib\charsets.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\deploy.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\dnsns.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\jaccess.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\localedata.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\nashorn.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunec.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\zipfs.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\javaws.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jce.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jfr.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jfxswt.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jsse.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\management-agent.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\plugin.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\resources.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\rt.jar;D:\develop\IDEA_Workspace\CloudCode\out\production\PattemMoudle com.yldyyn.test.BookStore
----書店賣出去的書籍記錄如下---
書籍名稱:天龍八部  書籍作者:金庸  書籍價格:¥44.00元
書籍名稱:射雕英雄傳  書籍作者:金庸  書籍價格:¥76.00元
書籍名稱:鋼鐵是怎么煉成的  書籍作者:保爾·柯查金  書籍價格:¥75.00元
書籍名稱:紅樓夢  書籍作者:曹雪芹  書籍價格:¥33.00元

Process finished with exit code 0

但是如果說現(xiàn)在書店賣書的時候要求打折出售,40以上的我們要7折售賣,40以下的我們打8折。

方法有三種,第一個辦法:修改接口。在 Books 上新增加一個方法 getOnSalePrice(),專門進行打折,所有實現(xiàn)類實現(xiàn)這個方法。但是這樣修改的后果就是實現(xiàn)類 NovelBook 要修改, BookStore 中的 main 方法也修改,同時 Books 作為接口應該是穩(wěn)定且可靠的,不應該經(jīng)常發(fā)生變化,否則接口做為契約的作用就失去了效能,其他不想打折的書籍也會因為實現(xiàn)了書籍的接口必須打折,因此該方案被否定。

第二個辦法:修改實現(xiàn)類。修改 NovelBook 類中的方法,直接在 getPrice() 中實現(xiàn)打折處理,這個應該是大家在項目中經(jīng)常使用的就是這樣辦法,通過 class 文件替換的方式可以完成部分業(yè)務(或是缺陷修復)變化,但是該方法還是有缺陷的,例如采購書籍人員也是要看價格的,由于該方法已經(jīng)實現(xiàn)了打折處理價格,因此采購人員看到的也是打折后的價格,這就產(chǎn)生了信息的蒙蔽效果,導致信息不對稱而出現(xiàn)決策失誤的情況。該方案也不是一個最優(yōu)的方案。

第三個辦法,通過擴展實現(xiàn)變化增加一個子類 OffNovelBook,覆寫 getPrice 方法,高層次的模塊(也就是 static 靜態(tài)模塊區(qū))通過 OffNovelBook 類產(chǎn)生新的對象,完成對業(yè)務變化開發(fā)任務。好辦法,風險也小。

public class OnSaleBook extends NovelBook {

    public OnSaleBook(String name, int price, String author) {
        super(name, price, author);
    }

    @Override
    public String getName() {
        return super.getName();
    }

    @Override
    public int getPrice() {
        int OnsalePrice = super.getPrice();
        int salePrce = 0;
        if (OnsalePrice >4000){
            salePrce = OnsalePrice * 70/100;
        }else{
            salePrce = OnsalePrice * 80/100;
        }
        return  salePrce;
    }

    @Override
    public String getAuthor() {
        return super.getAuthor();
    }
}

上面的代碼是擴展出來的一個類,而不是在原來的類中進行的修改。

public class BookStore {
    private final static ArrayList<Books> sBookList = new ArrayList<Books>();

    static {
        sBookList.add(new OnSaleBook("天龍八部", 4400, "金庸"));
        sBookList.add(new OnSaleBook("射雕英雄傳", 7600, "金庸"));
        sBookList.add(new OnSaleBook("鋼鐵是怎么煉成的", 7500, "保爾·柯查金"));
        sBookList.add(new OnSaleBook("紅樓夢", 3300, "曹雪芹"));
    }

    public static void main(String[] args) throws IOException {
        NumberFormat format = NumberFormat.getCurrencyInstance();
        format.setMaximumFractionDigits(2);
       System.out.println("----書店賣出去的書籍記錄如下---");
        for (Books book : sBookList) {
            System.out.println("書籍名稱:" + book.getName()
                    + "\t書籍作者:" + book.getAuthor()
                    + "\t書籍價格:" + format.format(book.getPrice() / 100.00) + "元");
        }
    }
}

結(jié)果展示:

----書店賣出去的書籍記錄如下---
書籍名稱:天龍八部  書籍作者:金庸  書籍價格:¥30.80元
書籍名稱:射雕英雄傳  書籍作者:金庸  書籍價格:¥53.20元
書籍名稱:鋼鐵是怎么煉成的  書籍作者:保爾·柯查金  書籍價格:¥52.50元
書籍名稱:紅樓夢  書籍作者:曹雪芹  書籍價格:¥26.40元

Process finished with exit code 0

在開閉原則中,抽象化是一個關鍵,解決問題的關鍵在于抽象化,在 JAVA 語言這種面向?qū)ο蟮恼Z言中,可以給系統(tǒng)定義出一個一勞永逸的,不再更改的抽象化的設計,此設計允許擁有無窮無盡的實現(xiàn)層被實現(xiàn)。

在 JAVA 語言中,可以給出一個或者多個抽象的 JAVA 類或者是JAVA接口,規(guī)定所有的具體類必須提供方法特征作為系統(tǒng)設計的抽象層,這個抽象層會遇見所有的可能出現(xiàn)的擴展,因此,在任何擴展情況下都不回去改變,這就讓系統(tǒng)的抽象層不需要修改,從而滿足開閉原則的第二條,對修改進行閉合。

同時,從抽象層里面導出一個或者多個新的具體類可以改變系統(tǒng)的行為,這樣就滿足了開閉原則的第一條。

盡管很多時候我們無法百分百的做到開閉原則,但是如果向著這個方向去努力,就能夠有部分的成功,這也是可以改善系統(tǒng)的結(jié)構(gòu)的。

責任編輯:武曉燕 來源: Java極客技術(shù)
相關推薦

2023-12-26 18:22:05

RocketMQ延遲消息

2020-02-04 15:00:25

大白話認識JVM

2021-02-18 09:06:39

數(shù)據(jù)訪問者模式

2012-03-15 11:15:13

Java設計模式

2020-09-08 06:30:59

微服務代碼模塊

2020-12-04 06:40:46

Zookeeper選舉機制

2020-02-20 11:32:09

Kafka概念問題

2024-04-24 12:41:10

Rust安全性內(nèi)存

2019-08-14 09:13:38

中臺互聯(lián)網(wǎng)業(yè)務

2019-05-17 08:27:23

SQL注入漏洞攻擊

2024-12-13 08:28:45

設計模式依賴

2021-03-01 18:38:32

Mock測試軟件

2023-09-18 14:34:07

Kubernetes云原生

2021-01-27 13:50:17

AI 數(shù)據(jù)機器學習

2018-11-19 08:34:22

Hadoop架構(gòu)HDFS

2023-12-18 10:08:56

2023-09-13 09:02:22

PVPVC存儲

2021-10-08 08:58:35

物聯(lián)網(wǎng)通信發(fā)布者

2023-05-06 07:29:49

Spring事務傳播

2011-07-04 17:59:03

開閉原則
點贊
收藏

51CTO技術(shù)棧公眾號