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

聊聊設計模式中的里式替換

開發(fā) 前端
依賴倒置原則是OO設計的核心原則,設計模式的研究和應用是以依賴導致原則為知道原則的,在知識星球中的設計模式中我們將會一一給大家體現(xiàn)。

一、前言

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

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

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

之前我們看了單一職責原則和開閉原則,今天我們一起來聊聊里式替換原則和依賴倒置原則,千萬別小看這些設計原則,他在設計模式中會有很多體現(xiàn),所以理解好設計原則之后,那么設計模式,也會讓你更加的好理解一點。

二、里式替換原則

里式替換原則,簡單的說:如果對每一個類型為T1的對象o1,都有類型為T2的對象o2,使得以T1定義的所有程序P在所有的對象o1都代換成o2 時,程序P的行為沒有發(fā)生變化,那么類型 T2 是類型 T1 的子類型。

換句話來說,一個軟件實體如果使用一個基類的話,那么一定適用于其子類,而且它根本不會察覺出基類對象和子類對象的區(qū)別。

比如說,假設有兩個類,一個是Base類,另一個是Derived類,并且Derived類是Base的子類,那么一個方法如果可以接受一個基類對象b的話:method(Base b),那么它必然可以接受一個子類對象d,可以有 method1(d)。

里式替換原則是繼承復用的基石,只有當衍生類可以替換掉基類,軟件單位的功能不會受到影響的時候,基類才能真正被復用,而衍生類也才能夠在基類的基礎上增加新的行為。

我們通過一個例子來理解一下:

《西游記》中,美猴王下地府橋段,個位應該有印象把,到達閻王殿之后,拿到生死簿,把生死簿上所有的包括自己,還有其他的獼猴,所有的猴子猴算都給劃了,這也是導致之后真假美猴王橋段的前序。

畫個圖理解:

圖片圖片

很顯然,地府管理一切生靈的生死的方法都是通過類來進行區(qū)分的,比如孫悟空就是石猴,之后出現(xiàn)的那個六耳獼猴就是獼猴,但是他們都是屬于同一個類,猴類,就像下圖中。

圖片圖片

因此,孫悟空把猴類中有姓名的都從生死簿勾掉之后,顯然是因為勾魂小鬼們并不區(qū)分石猴類與獼猴類,就像下圖:

圖片圖片

換句話來說,只要是猴類適用的,獼猴和石猴都適用,這其實就是里式替換原則。

這是第一種解釋,還有第二個更加通俗易懂的解釋:所有引用基類的地方必須能透明地使用其子類的對象。

第二種定義比較通俗,容易理解:只要有父類出現(xiàn)的地方,都可以用子類來替代,而且不會出現(xiàn)任何錯誤和異常。但是反過來則不行,有子類出現(xiàn)的地方,不能用其父類替代。

public class TestA {
    public void fun(int a,int b){
        System.out.println(a+"+"+b+"="+(a+b));
    }
    public static void main(String[] args) {
        System.out.println("父類的運行結果");
        TestA a=new TestA();
        a.fun(1,2);
        //父類存在的地方,可以用子類替代
        //子類B替代父類A
        System.out.println("子類替代父類后的運行結果");
        TestB b=new TestB();
        b.fun(1,2);
    }
}
class TestB extends TestA{
    @Override
    public void fun(int a, int b) {
        System.out.println(a+"-"+b+"="+(a-b));
    }
}

大家肯定也都能猜出來結果是什么樣子的。

父類的運行結果
1+2=3
子類替代父類后的運行結果
1-2=-1
Process finished with exit code 0

我們想要的結果是“1+2=3”??梢钥吹?,方法重寫后結果就不是了我們想要的結果了,也就是這個程序中子類B不能替代父類A。這違反了里氏替換原則原則,從而給程序造成了錯誤。

子類中可以增加自己特有的方法

這個很容易理解,子類繼承了父類,擁有了父類和方法,同時還可以定義自己有,而父類沒有的方法。這是在繼承父類方法的基礎上進行功能的擴展,符合里氏替換原則。

public class TestA {
    public void fun(int a,int b){
        System.out.println(a+"+"+b+"="+(a+b));
    }

    public static void main(String[] args) {
        System.out.println("父類的運行結果");
        TestA a=new TestA();
        a.fun(1,2);
        //父類存在的地方,可以用子類替代
        //子類B替代父類A
        System.out.println("子類替代父類后的運行結果");
        TestB b=new TestB();
        b.fun(1,2);
        b.newFun();
    }
}
class TestB extends TestA{
    public void newFun(){
        System.out.println("這是子類的新方法...");
    }
}

這次運行出來的代碼結果就是我們意料中的內容了。

父類的運行結果
1+2=3
子類替代父類后的運行結果
1+2=3
這是子類的新方法...
Process finished with exit code 0

JAVA語言對里式替換原則支持的局限:

JAVA編譯器的檢查是有局限性的,為什么呢?舉個例子來說,描述一個物體大小的量有精度和準確度兩種屬性。所謂的精度,就是這個量的有效數(shù)字有多少位;而所謂的精準度,是這個量與真實的物體大小相符合到什么程度。

一個量可以有很高的精度,但是卻無法與真實物體的情況相吻合,JAVA語言編譯器能夠檢查的,僅僅是相當于精度的屬性而已,它沒有辦法去檢查這個量與真實物體的差距。

換一句話來說,JAVA編譯器不能檢查一個系統(tǒng)在實現(xiàn)和商業(yè)邏輯上是否滿足里式替換原則。

三、依賴倒置原則

如果說實現(xiàn)開閉原則的關鍵事抽象化,是面向對象設計的目標的話,依賴倒置原則就是這個面向對象設計的主要機制。

依賴倒置原則,簡單的說:抽象不應該依賴于細節(jié),細節(jié)應當依賴于抽象。換言之,要針對接口編程,而不是針對實現(xiàn)編程。

為什么要實現(xiàn)倒置?這也是我們看這個定義的時候產(chǎn)生的一些問題,那么我們就來說說。

簡單的來說,傳統(tǒng)的過程性系統(tǒng)的設計辦法傾向于使高層次的模塊依賴于低層次的模塊,抽象層依賴于具體層次,倒置原則是要把這個錯誤的依賴關系倒轉過來,這就是依賴倒置原則的由來。也是為什么要進行依賴倒置。

依賴倒置原則的實現(xiàn)方法

依賴倒置原則的目的是通過要面向接口的編程來降低類間的耦合性,所以我們在實際編程中只要遵循以下4點,就能在項目中滿足這個規(guī)則:

  • 每個類盡量提供接口或抽象類,或者兩者都具備。
  • 變量的聲明類型盡量是接口或者是抽象類。
  • 任何類都不應該從具體類派生。
  • 用繼承時盡量遵循里氏替換原則。

下面我們通過一些代碼實例(商品售賣)來進行理解:

class BeijingShop implements Shop{
        public String sell(){
            return "北京商店售賣:北京烤鴨,稻香村月餅";
        }
    }
    class ShanDongShop implements  Shop{
        @Override
        public String sell() {
            return "山東商店售賣:德州扒雞,煙臺蘋果";
        }
    }
    //如果說顧客去購買商品
class Customer{
    public void shopping(ShanDongShop shop){
        //購物
        System.out.println(shop.sell());
    }
}
//這是在山東商店購買,如果說是在北京商店購買就會這樣
class Customer{
    public void shopping(BeijingShop shop) {
        //購物
        System.out.println(shop.sell());
    }
}

這也是這種設計的存在缺陷,顧客每更換一家商店,都要修改一次代碼,這明顯違背了開閉原則。存在以上缺點的原因是:顧客類設計時同具體的商店類綁定了,這違背了依賴倒置原則。解決方式我們可以定義一個共同的接口Shop,就可以這樣了。

public class TestSale {
    public static void main(String[] args) {
        Customer c = new Customer();
        System.out.println("---顧客購買商品如下---");
        c.shopping(new ShanDongShop());
        c.shopping(new BeijingShop());
    }
}

interface Shop{
    //售賣方法
    public String sell();
}

class BeijingShop implements Shop{
    public String sell(){
        return "北京商店售賣:北京烤鴨,稻香村月餅";
    }
}

class ShanDongShop implements  Shop{
    @Override
    public String sell() {
        return "山東商店售賣:德州扒雞,煙臺蘋果";
    }
}

class Customer{
    public void shopping(Shop shop) {
        System.out.println(shop.sell());//購物
    }
}

程序運行結果:

---顧客購買商品如下---
山東商店售賣:德州扒雞,煙臺蘋果
北京商店售賣:北京烤鴨,稻香村月餅
Process finished with exit code 0

這樣,不管顧客類 Customer 訪問什么商店,或者增加新的商店,都不需要修改原有代碼了。

依賴倒置原則是OO設計的核心原則,設計模式的研究和應用是以依賴導致原則為知道原則的,在知識星球中的設計模式中我們將會一一給大家體現(xiàn)。

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

2024-12-09 08:18:33

2024-10-06 12:56:36

Golang策略設計模式

2023-03-21 07:57:37

Go語言設計模式

2023-11-07 12:00:05

分布式系統(tǒng)數(shù)據(jù)訪問

2024-01-31 08:41:43

異步設計項目

2021-12-28 19:05:41

路由服務治理

2022-12-14 08:06:08

2023-01-04 09:37:16

2025-03-20 09:54:47

2024-07-05 08:26:54

2012-08-30 09:07:33

設計模式

2010-11-26 16:17:48

設計模式JDK

2021-04-18 21:07:32

門面模式設計

2024-07-31 08:12:33

2012-04-10 10:04:26

并行編程

2023-10-26 09:02:30

框架設計模式

2022-09-07 08:18:26

分布式灰度方案分支號

2012-12-25 09:38:41

JavaScript設計模式

2010-01-21 09:08:53

.NET設計模式

2021-04-14 09:02:22

模式 設計建造者
點贊
收藏

51CTO技術棧公眾號