装饰者模式详

装饰者模式

​ 动态的将责任附加到对象身上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案

问题

看一个饮料喝调料的问题

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

你可能感兴趣的:(设计模式)