說明:本文為《設(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é)(功能)网棍。
裝飾模式的通用類圖如下:
其中有四個(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é)
- 裝飾模式是繼承的有力補(bǔ)充,比繼承靈活似将,在不改變原有對象的情況下获黔,動(dòng)態(tài)的給一個(gè)對象擴(kuò)展功能,即插即用在验;
- 通過使用不同裝飾類及這些裝飾類的排列組合,可以實(shí)現(xiàn)不同效果盏触;
- 裝飾器模式完全遵守開閉原則块饺;
- 裝飾模式會(huì)增加許多子類,過度使用會(huì)增加程序得復(fù)雜性辨嗽。
3. 裝飾模式的使用場景
- 需要擴(kuò)展一個(gè)類的功能淮腾,或給一個(gè)類增加附加功能屉佳。
- 需要動(dòng)態(tài)地給一個(gè)對象增加功能武花,這些功能可以再動(dòng)態(tài)地撤銷杈帐。
- 需要為一批的兄弟類進(jìn)行改裝或加裝功能,當(dāng)然是首選裝飾模式干旁。
4. 最佳實(shí)踐
裝飾模式是對繼承的有力補(bǔ)充。
和使用繼承來解決問題相比:
- 用繼承解決問題回怜,容易增加很多子類,靈活性差翔试,不易維護(hù)复旬。 -> 裝飾模式可以替代繼承,解決類膨脹問題壁涎。
- 繼承是靜態(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)圖
附2:代碼實(shí)現(xiàn) https://github.com/ooxiaoyan/DecoratorPattern