裝飾模式-為類動(dòng)態(tài)添加職責(zé)

裝飾模式定義

裝飾模式(Decorator Pattern)- 動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé),就增加功能來(lái)說(shuō),裝飾模式相比生成子類更為靈活

裝飾模式也可以叫做包裝器(Wrapper),可以給一個(gè)類或?qū)ο笤黾有袨?/p>

通常有兩種方式來(lái)給類或?qū)ο髞?lái)增加行為:

  • 繼承機(jī)制:通過(guò)繼承一個(gè)現(xiàn)有的類可以使得子類在擁有自己方法的同時(shí)還可以擁有父類的方法,但是這種方式是靜態(tài)的,用戶不能控制增加行為的方式和時(shí)機(jī)
  • 關(guān)聯(lián)機(jī)制:將一個(gè)對(duì)象嵌到另一個(gè)對(duì)象中,并由另一個(gè)對(duì)象來(lái)決定是否調(diào)用嵌入對(duì)象的行為來(lái)擴(kuò)展自己的行為,這個(gè)嵌入對(duì)象就是裝飾器(Decorator)

模式結(jié)構(gòu)

角色職責(zé)

  • 抽象構(gòu)件(Component):一個(gè)接口或抽象類,定義原始的對(duì)象
  • 具體被裝飾對(duì)象(ConcreteComponent):具體的構(gòu)件實(shí)現(xiàn),被裝飾的對(duì)象,可以給這個(gè)對(duì)象添加一些職責(zé)
  • 抽象裝飾類(Decorator):持有一個(gè)指向Component實(shí)例的引用,并定義一個(gè)與Component接口一致的接口
  • 具體裝飾者(ConcreteDecorator):具體的裝飾對(duì)象,負(fù)責(zé)給構(gòu)件對(duì)象添加職責(zé)

類圖結(jié)構(gòu)

代碼實(shí)現(xiàn)

大家在看視頻的時(shí)候,視頻網(wǎng)站都會(huì)在播放之前給你來(lái)一段小廣告... 然后告訴你充錢可以變強(qiáng),可以不用看廣告,有一些視頻像綜藝節(jié)目或電影在播放完后還會(huì)有彩蛋

抽象構(gòu)件,定義一個(gè)播放視頻的接口

public interface Video {

    /**
     * 播放
     */
    void play();
}

具體的構(gòu)件,被修飾的對(duì)象,寫一個(gè)播放電影的類

/**
 * Film  具體構(gòu)件,被裝飾的對(duì)象
 * @author zouxq
 */
public class Movie implements Video{

    @Override
    public void play() {
        System.out.println("看電影");
    }
}

抽象裝飾類

/**
 * 抽象裝飾類
 * @author zouxq
 */
public abstract class BaseVideoDecorator implements Video {

    private Video video;

    // 通過(guò)構(gòu)造函數(shù)傳遞被修飾者
    public BaseVideoDecorator(Video video) {
        this.video = video;
    }

    // 委托給修飾者執(zhí)行
    @Override
    public void play() {
        video.play();
    }
}

具體的裝飾類,用來(lái)給具體的構(gòu)件添加職責(zé),這里給播放視頻的對(duì)象添加播放廣告的職責(zé)

/**
 * 具體的修飾類
 * @author zouxq
 */
public class AdvertisingVideoDecorator extends BaseVideoDecorator {

    // 定義被修飾者
    public AdvertisingVideoDecorator(Video video) {
        super(video);
    }

    @Override
    public void play() {
        addAdvertising();
        super.play();
    }

    // 定義修飾的方法
    private void addAdvertising(){
        System.out.println("來(lái)看個(gè)廣告,充錢可以不用看哦");
    }

}

下面的裝飾類用來(lái)添加播放彩蛋的職責(zé)

/**
 * 具體的修飾類
 * @author zouxq
 */
public class EasterEggsVideoDecorator extends BaseVideoDecorator {

    // 定義被修飾者
    public EasterEggsVideoDecorator(Video video) {
        super(video);
    }

    @Override
    public void play() {
        super.play();
        addEggs();
    }

    // 定義修飾的方法
    private void addEggs(){
        System.out.println("加個(gè)彩蛋...");
    }

}

測(cè)試使用

@Test
public void decoratorTest() {
    Video movie = new Movie();
    movie.play();
    System.out.println("第一次裝飾......");
    movie = new AdvertisingVideoDecorator(movie);
    movie.play();
    System.out.println("第二次裝飾......");
    movie = new EasterEggsVideoDecorator(movie);
    movie.play();
}

測(cè)試結(jié)果

看電影
第一次裝飾......
來(lái)看個(gè)廣告,充錢可以不用看哦
看電影
第二次裝飾......
來(lái)看個(gè)廣告,充錢可以不用看哦
看電影
加個(gè)彩蛋...

在上面的例子,我們是把四個(gè)角色都寫出來(lái)了,但是實(shí)際運(yùn)用的時(shí)候根據(jù)實(shí)際情況還可以更加的靈活。比如,如果能確定被裝飾類就一個(gè)對(duì)象,可以只有一個(gè)ConcreteComponent類而不需要抽象的Component類,而Decorator類可以是ConcreteComponent的一個(gè)子類;同樣我們只需為添加一個(gè)職責(zé),就可以不要建立一個(gè)單獨(dú)的Decoratorl類,直接寫一個(gè)ConcreteDecorator就可以了

裝飾模式的優(yōu)缺點(diǎn)和使用場(chǎng)景

優(yōu)缺點(diǎn)

  • 裝飾類和被裝飾類可以相互獨(dú)立,不會(huì)相互耦合。裝飾類Decorator是從外部來(lái)擴(kuò)展被裝飾類Component的,不需要知道Component的內(nèi)部實(shí)現(xiàn)
  • 裝飾模式是繼承的一種替代方案,修飾返回的對(duì)象還是Component,是is-a的關(guān)系
  • 裝飾模式可以動(dòng)態(tài)的擴(kuò)展一個(gè)類的功能
  • 可以把原始類中的裝飾功能放到裝飾類中,把類的核心職責(zé)和裝飾功能區(qū)分開

裝飾模式的一大缺點(diǎn)就是:多層的裝飾會(huì)比較復(fù)雜,裝飾的順序也是很重要的,盡量要減少裝飾類的數(shù)量,避免太過(guò)復(fù)雜

使用場(chǎng)景

  • 需要擴(kuò)展一個(gè)類的功能
  • 動(dòng)態(tài)的給對(duì)象增加功能,并且可以動(dòng)態(tài)的撤銷(繼承是靜態(tài)擴(kuò)展)

為什么選擇裝飾模式而不是繼承

從上面的例子和總結(jié),我們可以看出使用裝飾模式的好處,那么裝飾模式和繼承同樣是可以擴(kuò)展類的功能,為什么會(huì)更偏向使用裝飾模式

我們用一個(gè)例子來(lái)說(shuō)明一下,我們?nèi)ワ嬈返耆ベI一杯飲料,比如買一杯奶茶MilkyTea,這時(shí)服務(wù)員問(wèn)你想要加什么,可以加紅豆、椰奶、珍珠... 至于加料之后的奶茶我們很容易想到使用繼承去實(shí)現(xiàn),比如紅豆OrmosiaMilkyTea、椰奶CoconutMilkyTea、珍珠PearlMilkyTea,假如要加多種料呢?紅豆椰奶OrmosiaCoconutMilkyTea ......

到這里就會(huì)發(fā)現(xiàn),使用繼承會(huì)要寫許多的類,這里才3種加料,假如是可以加十幾二十種配料呢,那自由組合的可能性就太多了,使用繼承就會(huì)使我們的子類爆炸。使用繼承我們要添加的功能是靜態(tài)的,添加的功能便是在子類中實(shí)現(xiàn),在編譯時(shí)就已經(jīng)確定下來(lái);而使用裝飾模式則是動(dòng)態(tài)的添加功能,我需要什么功能便使用對(duì)應(yīng)的裝飾類去裝飾,裝飾的種類和順序都是在客戶端動(dòng)態(tài)的加載,更加的靈活

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 【學(xué)習(xí)難度:★★★☆☆,使用頻率:★★★☆☆】直接出處:裝飾模式梳理和學(xué)習(xí):https://github.com/...
    BruceOuyang閱讀 847評(píng)論 2 2
  • 0x01 前言 ??裝飾器模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變...
    菩提樹下成魔閱讀 478評(píng)論 0 0
  • 模式動(dòng)機(jī) 一般有兩種方式可以實(shí)現(xiàn)給一個(gè)類或?qū)ο笤黾有袨椋?繼承機(jī)制,使用繼承機(jī)制是給現(xiàn)有類添加功能的一種有效途徑,...
    lijun_m閱讀 659評(píng)論 0 0
  • 最近最讓大家提心吊膽的莫過(guò)于購(gòu)房政策與房?jī)r(jià)的起伏,據(jù)說(shuō)全國(guó)已經(jīng)有20多座城市的房?jī)r(jià)已經(jīng)到了瘋漲的狀態(tài),如同運(yùn)動(dòng)員吃...
    大叔有文化閱讀 313評(píng)論 0 0
  • 我的家是在鄉(xiāng)下,但我們有一家親戚是在城里。每到寒暑假,我就會(huì)到親戚家去玩一段時(shí)間。親戚家有三個(gè)孩子,最小的孩子比我...
    一切都正好閱讀 351評(píng)論 0 0

友情鏈接更多精彩內(nèi)容