裝飾者模式詳

裝飾者模式

​ 動態的將責任附加到對象身上,若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案

問題

看一個飲料喝調料的問題

1,消費者需要一杯咖啡

2,消費者提出要求:要加糖

3,消費者提出要求:要加兩份牛奶

這個時候你會用什麼方式來解決呢?

解決思路

1,使用最原始的方法,記住每一種調料的價格和飲料的價格,最後+在一起。

  • 如果消費者忽然不要某種飲料呢?

​ 減去相應的價格

  • 或者說想要發票?

​ 。。。。

  • 如果飲料種類非常多呢?

​ 。。。。

怎麼辦呢?

2,使用裝飾者,把飲料看做爲被裝飾者,調料爲裝飾者。

​ 如果消費者忽然不要某種飲料呢?

​ 刪除對應的裝飾者

​ 或者說想要發票?

​ 每種調料內都添加描述即可

​ 種類非常多

​ 創建 package 進行分類,如 碳酸飲料:,分爲哪幾種(可樂,雪碧等在一個package),調料分爲哪幾種(加冰,薑汁等,在一個 package中)!

實現

1,首先定義飲料的抽象類

/**
 * @name ModeDemo
 * @class name:com.example.mode.decorate
 * @author 345 QQ:1831712732
 * @time 2020/1/6 21:29
 * @description 飲料
 */

abstract class Beverage {
    /**
     * 未知的飲料
     */
    var mDescription = "Unknown Beverage"

    /**
     * 獲取飲料描述
     */
    open fun getDescription(): String {
        return mDescription
    }

    /**
     * 計算價格
     */
    abstract fun cost(): Double
}

2,創建飲料

/**
 * @name ModeDemo
 * @class name:com.example.mode.decorate
 * @author 345 QQ:1831712732
 * @time 2020/1/6 21:36
 * @description 被裝飾者:濃縮咖啡
 */
class Espresso : Beverage() {


    init {
        mDescription = "濃縮咖啡"
    }

    /**
     * 濃縮咖啡的價格
     */
    override fun cost(): Double {
        return 32.00
    }
}
/**
 * @author 345 QQ:1831712732
 * @name ModeDemo
 * @class name:com.example.mode.decorate
 * @time 2020/1/6 21:38
 * @description 被裝飾者:混合咖啡
 */
class HouseBlend : Beverage() {

    init {
        mDescription = "混合咖啡"
    }


    override fun cost(): Double {
        return 50.00
    }

}

3,創建調料抽象類

/**
 * @name ModeDemo
 * @class name:com.example.mode.decorate
 * @author 345 QQ:1831712732
 * @time 2020/1/6 21:33
 * @description 裝飾者類(調料),例如給咖啡添加牛奶,糖等,需要繼承此類
 */
abstract class CondimentDecorator : Beverage() {

    /**
     * 調料的描述
     */
    abstract override fun getDescription(): String
}

4,創建具體的調料類

/**
 * @name ModeDemo
 * @class name:com.example.mode.decorate.seasoning
 * @author 345 QQ:1831712732
 * @time 2020/1/6 21:46
 * @description 裝飾者:牛奶
 */
class Milk(private val beverage: Beverage) : CondimentDecorator() {
    override fun getDescription(): String {
        return beverage.getDescription() + ",牛奶"
    }

    /**
     * 價格
     */
    override fun cost(): Double {
        return 5.00 + beverage.cost()
    }

}
/**
 * @name ModeDemo
 * @class name:com.example.mode.decorate.seasoning
 * @author 345 QQ:1831712732
 * @time 2020/1/6 21:55
 * @description 裝飾者:糖
 */
class Sugar(private val beverage: Beverage) : CondimentDecorator() {
    /**
     * 描述
     */
    override fun getDescription(): String {
        return beverage.getDescription() + ",糖"
    }

    /**
     * 價格
     */
    override fun cost(): Double {
        return 4.00 + beverage.cost()
    }
}
/**
 * @name ModeDemo
 * @class name:com.example.mode.decorate.seasoning
 * @author 345 QQ:1831712732
 * @time 2020/1/6 21:58
 * @description 裝飾者:奶油
 */
class Whip(private val beverage: Beverage) : CondimentDecorator() {

    override fun getDescription(): String {
        return beverage.getDescription() + ",奶油"
    }

    /**
     * jia價格
     */
    override fun cost(): Double {
        return 6.00 + beverage.cost()
    }
}

5,測試

/**
 * @name DesignModeDemo
 * @class name:com.example.mode.decorate
 * @author 345 QQ:1831712732
 * @time 2020/1/6 22:00
 * @description
 */

fun main() {
    /**
     * 一杯濃縮咖啡,加糖,牛奶
     */
    val milk = Milk(Sugar(Espresso()))
    println(milk.getDescription() + "    ¥" + milk.cost())

    /**
     * 一杯杯混合咖啡,加牛奶,兩份奶油
     */
    val whip = Whip(Whip(Milk(HouseBlend())))
    println(whip.getDescription() + "    ¥" + whip.cost())
}

結果

濃縮咖啡,糖,牛奶    ¥41.0
混合咖啡,牛奶,奶油,奶油    ¥67.0

解決問題

**1,**如果消費者忽然不要某種飲料呢?例如:兩份牛奶換成一份

​ 這個比較麻煩了,需要修改代碼(一般情況不會這樣做,下面會給出解釋)。給調料抽象類添加如下代碼:

abstract class CondimentDecorator : Beverage() {

    /**
     * 調料的描述
     */
    abstract override fun getDescription(): String

    abstract fun setCondiment(condiment: CondimentDecorator)
}

​ 實現類

class Whip(private var beverage: Beverage) : CondimentDecorator() {
    override fun setCondiment(condiment: CondimentDecorator) {
        beverage = condiment
    }

    override fun getDescription(): String {
        return beverage.getDescription() + ",奶油"
    }

    /**
     * 價格
     */
    override fun cost(): Double {
        return 6.00 + beverage.cost()
    }
}

​ 其他的實現類都是如此

​ 測試

fun main() {

    /**
     * 一杯杯混合咖啡,加牛奶,兩份奶油
     */
    val whip = Whip(Whip(Milk(HouseBlend())))
    println(whip.getDescription() + "    ¥" + whip.cost())

    val houseBlend = HouseBlend()
    val listOf = mutableListOf<CondimentDecorator>()
    //兩份奶油,一份牛奶
    listOf += Whip(houseBlend)
    listOf += Whip(houseBlend)
    listOf += Milk(houseBlend)

    for (i in 0 until listOf.size) {
        if (i < listOf.size - 1) {
            listOf[i].setCondiment(listOf[i + 1])
        }
    }
    println(listOf[0].getDescription() + "    ¥" + listOf[0].cost())
    //取消一個牛奶,這裏可以直接刪除引用
    listOf.removeAt(0)
    println(listOf[0].getDescription() + "    ¥" + listOf[0].cost())
}
混合咖啡,牛奶,奶油,奶油    ¥67.0
混合咖啡,牛奶,奶油,奶油    ¥67.0
混合咖啡,牛奶,奶油    ¥61.0

​ 看起來麻煩了一些。其實代碼還是很好理解的

​ 原來都是通過構造方法進行裝飾,但是現在增加了一個 set 方法。通過 set 也可以進行裝飾

​ 代碼還可以進行更好的優化

​ 當然了,一般情況下也不需要這種代碼。因爲裝飾是一次性的。就像 JAVA 的 IO 一樣,也是用的是裝飾者模式,你不可能裝飾後在減掉某個裝飾板。這裏只是一個擴展

2,3

這兩個問題都差不多。只要添加相應的調料和飲料即可。

要點

  • 裝飾者模式意味着一羣裝飾者類,這些類用於包裝具體的組件
  • 你可以使用無數個裝飾者包裝一個組件
  • 裝飾者會導致程序中出現過多的小對象。如果過度使用,會讓程序變得很複雜。
  • 一旦裝飾後,一般情況下,就不能取消某個裝飾了。當然你可以使用我上面的做法,並做一下適當的修改。但是不推薦這種做法

參考自 Head First

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章