包裝模式就是這麼簡單啦

前言

只有光頭才能變強數據庫

回顧前面:設計模式

前一篇已經講解了代理模式了,今天要講解的就是裝飾模式啦~微信

在看到FilterInputStreamFilterOutputStream時看到了以前常聽見的裝飾模式(對IO必定了解的同窗可能都會知道那麼一句話:在IO用得最多的就是裝飾模式了)!ide

其實不管是代理模式仍是裝飾模式。本質上我認爲就是對原有對象加強的方式~函數

那麼接下來就開始吧,若是文章有錯誤的地方請你們多多包涵,不吝在評論區指正哦~post

聲明:本文使用JDK1.8this

1、對象加強的經常使用方式

不少時候咱們可能對Java提供給咱們的對象不滿意,不能知足咱們的功能。此時咱們就想對Java原對象進行加強,可以實現咱們想要的功能就好~spa

通常來講,實現對象加強有三種方式:設計

1.1繼承

最簡單的方式就是繼承父類,子類擴展來達到目的。雖然簡單,可是這種方式的缺陷很是大代理

  • 1、若是父類是帶有數據、信息、屬性的話,那麼子類沒法加強。
  • 2、子類實現了以後需求沒法變動,加強的內容是固定的。

1.1.1第一點

第一點就拿之前在學JDBC的時候來講:

  • 當時想要本身寫一個簡易的JDBC鏈接池,鏈接池由List<Connection>來管理。顯然咱們的對象是Connection,當寫到close()方法的時候卡住了。
  • 由於咱們想要的功能是:調用close()是讓咱們的Connection返回到「鏈接池」(集合)中,而不是關閉掉。
  • 此時咱們不能使用繼承父類的方式來實現加強。由於Connection對象是由數據庫廠商來實現的,在獲得Connection對象的時候綁定了各類信息(數據庫的username、password、具體的數據庫是啥等等)。咱們子類繼承Connection是沒法獲得對應的數據的!就更別說調用close()方法了。

1.1.2第二點

第二點我也舉個例子:

如今我設計一個電話類:

public class Phone {
    // 能夠打電話
    public void call() {
        System.out.println("打電話給周圍的人關注公衆號Java3y");
    }
}

此時,我想打電話以前能聽彩鈴,因而我繼承Phone類,實現我想要的功能。

public class MusicPhone extends Phone {
    
    // 聽彩鈴
    public void listenMusic() {
        System.out.println("我懷念的是無話不說,我懷念的是一塊兒作夢~~~~~~");
    }

    @Override
    public void call() {

        // 在打電話以前聽彩鈴
        listenMusic();

        super.call();
    }
}

咱們的功能就作好了:

 

 

  • 此時,我又忽然想實現多一個需求了,我想要聽完電話以後告訴我一下當前的時間是多少。沒事,咱們又繼承來加強一下:
// 這裏繼承的是MusicPhone類
public class GiveCurrentTimePhone extends MusicPhone {

    // 給出當前的時間
    public void currentTime() {
        System.out.println("當前的時間是:" + System.currentTimeMillis());
    }

    @Override
    public void call() {
        super.call();

        // 打完電話提示如今的時間是多少啦
        currentTime();
    }
}

因此咱們仍是能夠完成任務滴:

 

 

但是我需求如今又想變了:

  • 我不想聽彩鈴了,只想聽完電話通知一下時間就行了........(但是咱們的通知時間電話類是繼承在聽彩鈴的電話類基礎之上的),,,
  • 我又有可能:我想在聽電話以前報告一下時間,聽完電話聽音樂!...
  • 若是需求變更很大的狀況下,而咱們又用繼承的方式來實現這樣會致使一種現象:類爆炸(類數量激增)!而且繼承的層次可能會比較多~

因此,咱們能夠看到子類繼承父類這種方式來擴展是十分侷限的,不靈活的~

所以咱們就有了裝飾模式

1.2裝飾模式

首先咱們來看看裝飾模式是怎麼用的吧。

1.2.1前提代碼

電話接口:

// 一個良好的設計是抽取成接口或者抽象類的
public interface Phone {

    // 能夠打電話
    void call();
}

具體的實現

public class IphoneX implements Phone {


    @Override
    public void call() {
        System.out.println("打電話給周圍的人關注公衆號Java3y");
    }
}

1.2.2包裝模式實現

上面咱們已經擁有了一個接口還有一個默認實現。包裝模式是這樣乾的:

首先咱們弄一個裝飾器,它實現了接口,以組合的方式接收咱們的默認實現類

// 裝飾器,實現接口
public abstract class PhoneDecorate implements Phone {

    // 以組合的方式來獲取默認實現類
    private Phone phone;
    public PhoneDecorate(Phone phone) {
        this.phone = phone;
    }

    @Override
    public void call() {
        phone.call();
    }
}

有了裝飾器之後,咱們的擴展均可以以裝飾器爲基礎進行擴展,繼承裝飾器來擴展就行了!

咱們想要在打電話以前聽音樂

// 繼承着裝飾器來擴展
public class MusicPhone extends PhoneDecorate {

    public MusicPhone(Phone phone) {
        super(phone);
    }

    // 定義想要擴展的功能
    public void listenMusic() {

        System.out.println("繼續跑 帶着赤子的驕傲,生命的閃耀不堅持到底怎能看到,與其苟延殘喘不如縱情燃燒");

    }

    // 重寫打電話的方法
    @Override
    public void call() {

        // 在打電話以前聽音樂
        listenMusic();
        super.call();
    }
}

 

 

如今我也想在打完電話後通知當前的時間,因而咱們也繼承裝飾類來擴展

// 這裏繼承的是MusicPhone裝飾器類
public class GiveCurrentTimePhone extends PhoneDecorate  {


    public GiveCurrentTimePhone(Phone phone) {
        super(phone);
    }

    // 自定義想要實現的功能:給出當前的時間
    public void currentTime() {
        System.out.println("當前的時間是:" + System.currentTimeMillis());
    }

    // 重寫要加強的方法
    @Override
    public void call() {
        super.call();
        // 打完電話後通知一下當前時間
        currentTime();
    }
}

能夠完成任務:

 

 

目前這樣看起來,比我直接繼承父類要麻煩,而功能效果是同樣的....咱們繼續往下看~~

此時,我不想在打電話以前聽到彩鈴了,很簡單:咱們不裝飾它就行了!

 

 

此時,我想在打電話前報告一下時間,在打完電話以後聽彩鈴。

  • 注意:雖說要改動類中的代碼,可是這種改動是合理的。由於我定義出的GiveCurrentTimePhone類MusicPhone類自己從語義上就沒有規定擴展功能的執行順序
  • 而繼承不同:先繼承Phone->實現MusicPhone->再繼承MusicPhone實現GiveCurrentTimePhone。這是固定的,從繼承的邏輯上已經寫死了具體的代碼,是難以改變的。

 

 

 

 

因此咱們仍是能夠很簡單地完成功能:

 

 

2、裝飾模式講解

可能有的同窗在看完上面的代碼以後,仍是迷迷糊糊地不知道裝飾模式是怎麼實現「裝飾」的。下面我就再來解析一下:

  • 第一步:咱們有一個Phone接口,該接口定義了Phone的功能
  • 第二步:咱們有一個最簡單的實現類iPhoneX
  • 第三步:寫一個裝飾器抽象類PhoneDecorate,以組合(構造函數傳遞)的方式接收咱們最簡單的實現類iPhoneX。其實裝飾器抽象類的做用就是代理(核心的功能仍是由最簡單的實現類iPhoneX來作,只不過在擴展的時候能夠添加一些沒有的功能而已)。
  • 第四步:想要擴展什麼功能,就繼承PhoneDecorate裝飾器抽象類,將想要加強的對象(最簡單的實現類iPhoneX或者已經被加強過的對象)傳進去,完成咱們的擴展!

再來看看下面的圖,就懂了!

 

 

每每咱們的代碼能夠省略起來,成了這個樣子(是否是和IO的很是像!)

// 先加強聽音樂的功能,再加強通知時間的功能
    Phone phone = new GiveCurrentTimePhone(new MusicPhone(new IphoneX()));

結果是同樣的:

 

 

2.1裝飾模式的優缺點

優勢:

  • 裝飾類和被裝飾類是能夠獨立的,低耦合的。互相都不用知道對方的存在
  • 裝飾模式是繼承的一種替代方案,不管包裝多少層,返回的對象都是is-a的關係(上面的例子:包裝完仍是Phone類型)。
  • 實現動態擴展,只要繼承了裝飾器就能夠動態擴展想要的功能了。

缺點:

  • 多層裝飾是比較複雜的,提升了系統的複雜度。不利於咱們調試~

3、總結

最後來補充一下包裝模式和代理模式的類圖:

 

 

 

 

對象加強的三種方式:

  • 繼承
  • 包裝模式
  • 代理模式

那麼只要遇到Java提供給咱們的API不夠用,咱們加強一下就好了。在寫代碼時,某個類被寫死了,功能不夠用,加強一下就能夠了!

理解包裝模式,接下來就開始IO之旅咯~~~

參考資料:

若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y

文章的目錄導航

相關文章
相關標籤/搜索