裝飾模式(Decorator Pattern)

說明:本文為《設(shè)計(jì)模式之禪》的閱讀筆記材鹦,主要總結(jié)精華和記錄自己的部分理解末捣。代碼部分由Kotlin實(shí)現(xiàn)。

1. 定義

Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.
動(dòng)態(tài)地給一個(gè)對象添加一些額外的職責(zé)箩做。 就增加功能來說妥畏,裝飾模式相比生成子類更為靈活。

也有叫“裝飾器模式”醉蚁。

通俗理解:在不改變現(xiàn)有對象結(jié)構(gòu)的前提下,動(dòng)態(tài)的給該對象增加一些額外的職責(zé)(功能)网棍。

裝飾模式的通用類圖如下:


裝飾模式通用類圖.png

其中有四個(gè)角色

  • Component抽象構(gòu)件
    Component是一個(gè)接口或抽象類,是我們定義的最核心的對象滥玷,也是最原始的對象。(是我們想要增加額外職責(zé)的對象)

注意 在裝飾模式中惑畴,必然有一個(gè)最基本、最核心如贷、最原始的接口或抽象類充當(dāng) Component抽象構(gòu)件。

  • ConcreteComponent具體構(gòu)件
    ConcreteComponent是最核心杠袱、最原始尚猿、最基本的接口或抽象類(Component)的實(shí)現(xiàn),我們要裝飾的就是這個(gè)對象楣富。

  • Decorator裝飾角色
    一般是一個(gè)抽象類凿掂,實(shí)現(xiàn)接口或者抽象方法,在它的屬性里必然有一個(gè)private變量指向Component抽象構(gòu)件菩彬。
    如果只有一個(gè)裝飾類缠劝,則可以沒有抽象裝飾角色,直接實(shí)現(xiàn)具體的裝飾角色即可骗灶。

  • 具體裝飾角色
    ConcreteDecoratorA和ConcreteDecoratorB是兩個(gè)具體的裝飾類惨恭,我們要把你最核心的、最原始的耙旦、最基本的東西裝飾成其他東西脱羡。

下面用kotlin實(shí)現(xiàn)一下通用的裝飾模式萝究,如代碼清單1.
代碼清單1 裝飾模式通用代碼

// 抽象構(gòu)件
abstract class Component {

    abstract fun operate()
}

// 具體構(gòu)件
class ConcreteComponent : Component() {

    // 具體實(shí)現(xiàn)
    override fun operate() {
        println("do something in ConcreteComponent")
    }
}

// 抽象裝飾者
// 如果只有一個(gè)裝飾類,則可以沒有抽象裝飾角色锉罐,直接實(shí)現(xiàn)具體的裝飾角色即可
abstract class Decorator(protected open val component: Component) : Component() {

    // 委托給被修飾者執(zhí)行
    override fun operate() {
        component.operate()
    }
}

// 具體的裝飾類
class ConcreteDecorator1(component: Component): Decorator(component) {

    // 重寫父類的Operation方法
    override fun operate() {
        this.method1()
        super.operate()
    }

    // 定義自己的修飾方法
    private fun method1() {
        println("method1 修飾 in ConcreteDecorator1")
    }
}

// 具體的裝飾類
class ConcreteDecorator2(component: Component): Decorator(component) {

    // 重寫父類的Operation方法
    override fun operate() {
        super.operate()
        this.method2()
    }

    // 定義自己的修飾方法
    private fun method2() {
        println("method2 修飾 in ConcreteDecorator2")
    }
}

// 場景類
fun main(args: Array<String>) {
    var component: Component = ConcreteComponent()
    // 第一次修飾
    component = ConcreteDecorator1(component)
    // 第二次修飾
    component = ConcreteDecorator2(component)
    // 修飾后運(yùn)行
    component.operate()
}

運(yùn)行結(jié)果:

method1 修飾 in ConcreteDecorator1
do something in ConcreteComponent
method2 修飾 in ConcreteDecorator2

Process finished with exit code 0

注意 原始方法和裝飾方法的執(zhí)行順序在具體的裝飾類是固定的帆竹,可以通過方法重載實(shí)現(xiàn)多種執(zhí)行順序。

再來看一個(gè)實(shí)際的例子脓规,如代碼清單2:
代碼清單2 咖啡機(jī)

// 抽象構(gòu)件-咖啡機(jī)
interface CoffeeMachine {

    fun makeEspresso()
    fun makeDoubleEspresso()
}

// 具體構(gòu)件-普通咖啡機(jī)
class NormalCoffeeMachine: CoffeeMachine {

    override fun makeEspresso() {
        println("make espresso")
    }

    override fun makeDoubleEspresso() {
        println("make double espresso twice")
    }
}

// 裝飾類
class EnhancedCoffeeMachine(private val coffeeMachine: CoffeeMachine) : CoffeeMachine by coffeeMachine {

    override fun makeDoubleEspresso() {
        println("once time")
        println("make double espresso")
    }

    fun makeCoffeeWithMilk() {
        coffeeMachine.makeEspresso()
        println("add milk")
    }
}

// 場景類
fun main(args: Array<String>) {

    val normalCoffeeMachine = NormalCoffeeMachine()
    val enhancedCoffeeMachine = EnhancedCoffeeMachine(normalCoffeeMachine)

    enhancedCoffeeMachine.makeEspresso() // 非重寫
    enhancedCoffeeMachine.makeDoubleEspresso() // 重寫了
    enhancedCoffeeMachine.makeCoffeeWithMilk() 
}

運(yùn)行結(jié)果:

make espresso
once time
make double espresso
make espresso
add milk

2. 裝飾模式的優(yōu)缺點(diǎn)

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

  • 裝飾類和被裝飾類可以獨(dú)立發(fā)展栽连,而不會(huì)相互耦合。
    Component類無須知道Decorator類侨舆,Decorator類是從外部來擴(kuò)展Component類的功能秒紧,而Decorator也不用知道具體的構(gòu)件。

  • 裝飾模式是繼承關(guān)系的一個(gè)替代方案挨下。

  • 裝飾模式可以動(dòng)態(tài)地?cái)U(kuò)展一個(gè)實(shí)現(xiàn)類的功能熔恢。

2.1 缺點(diǎn)

  • 多層的裝飾是比較復(fù)雜的。
    為什么會(huì)復(fù)雜呢臭笆?你想想看叙淌,就像剝洋蔥一樣,你剝到了最后才發(fā)現(xiàn)是最里層的裝飾出現(xiàn)了問題愁铺,想象一下工作量吧鹰霍,因此,盡量減少裝飾類的數(shù)量帜讲,以便降低系統(tǒng)的復(fù)雜度衅谷。

裝飾模式特點(diǎn)總結(jié)

  1. 裝飾模式是繼承的有力補(bǔ)充,比繼承靈活似将,在不改變原有對象的情況下获黔,動(dòng)態(tài)的給一個(gè)對象擴(kuò)展功能,即插即用在验;
  2. 通過使用不同裝飾類及這些裝飾類的排列組合,可以實(shí)現(xiàn)不同效果盏触;
  3. 裝飾器模式完全遵守開閉原則块饺;
  4. 裝飾模式會(huì)增加許多子類,過度使用會(huì)增加程序得復(fù)雜性辨嗽。

3. 裝飾模式的使用場景

  • 需要擴(kuò)展一個(gè)類的功能淮腾,或給一個(gè)類增加附加功能屉佳。
  • 需要動(dòng)態(tài)地給一個(gè)對象增加功能武花,這些功能可以再動(dòng)態(tài)地撤銷杈帐。
  • 需要為一批的兄弟類進(jìn)行改裝或加裝功能,當(dāng)然是首選裝飾模式干旁。

4. 最佳實(shí)踐

裝飾模式是對繼承的有力補(bǔ)充

和使用繼承來解決問題相比:

  1. 用繼承解決問題回怜,容易增加很多子類,靈活性差翔试,不易維護(hù)复旬。 -> 裝飾模式可以替代繼承,解決類膨脹問題壁涎。
  2. 繼承是靜態(tài)地給類增加功能 -> 裝飾模式是動(dòng)態(tài)增加功能(不想要可以在場景類中不要)

裝飾模式非常好的優(yōu)點(diǎn):擴(kuò)展性好

假設(shè)怔球,三個(gè)繼承關(guān)系Father浮还、Son、GrandSon三個(gè)類钧舌,我們需要在Son類上增強(qiáng)一些功能怎么辦洼冻?我想你會(huì)堅(jiān)決地頂回去!不允許驾荣,對了,為什么呢播掷?你增強(qiáng)的功能是修改Son類中的方法嗎?增加方法嗎垒酬?對GrandSon的影響呢件炉?特別是GrandSon 有多個(gè)的情況,你會(huì)怎么辦口糕?這個(gè)評估的工作量就夠你受的磕蛇,所以這是不允許的,那還是要解決問題的呀超棺,怎么辦呵燕?通過建立SonDecorator類來修飾Son,相當(dāng)于創(chuàng)建了一個(gè)新的類氧苍,這個(gè)對原有程序沒有變更候引,通過擴(kuò)展很好地完成了這次變更敦跌。

附1:思維導(dǎo)圖


裝飾模式 (Decorator Pattern).png

附2:代碼實(shí)現(xiàn) https://github.com/ooxiaoyan/DecoratorPattern

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末柠傍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惧笛,更是在濱河造成了極大的恐慌患整,老刑警劉巖喷众,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件到千,死亡現(xiàn)場離奇詭異赴穗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)了赵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門柿汛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來埠对,“玉大人,你說我怎么就攤上這事】杈浚” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵臣嚣,是天一觀的道長硅则。 經(jīng)常有香客問我株婴,道長,這世上最難降的妖魔是什么困介? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任徒扶,我火速辦了婚禮根穷,結(jié)果婚禮上导坟,老公的妹妹穿的比我還像新娘圈澈。我一直安慰自己,他們只是感情好闯两,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布漾狼。 她就那樣靜靜地躺著饥臂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪稽煤。 梳的紋絲不亂的頭發(fā)上酵熙,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天匾二,我揣著相機(jī)與錄音拳芙,去河邊找鬼。 笑死舟扎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的譬猫。 我是一名探鬼主播羡疗,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼顺囊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了诚亚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤闸准,失蹤者是張志新(化名)和其女友劉穎梢灭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敏释,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡义屏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年闽铐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奶浦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡察迟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出所踊,到底是詐尸還是另有隱情,我是刑警寧澤碌燕,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布修壕,位于F島的核電站遏考,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏青团。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一芦昔、第九天 我趴在偏房一處隱蔽的房頂上張望咕缎。 院中可真熱鬧料扰,春花似錦、人聲如沸记罚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽然磷。三九已至姿搜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捆憎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留础拨,地道東北人诡宗。 一個(gè)月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像蝠引,于是被迫代替她去往敵國和親立肘。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容