装饰者模式详解

简介: 装饰者模式详解

装饰者模式


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


问题


看一个饮料喝调料的问题


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


这两个问题都差不多。只要添加相应的调料和饮料即可。


要点


装饰者模式意味着一群装饰者类,这些类用于包装具体的组件

你可以使用无数个装饰者包装一个组件

装饰者会导致程序中出现过多的小对象。如果过度使用,会让程序变得很复杂。

一旦装饰后,一般情况下,就不能取消某个装饰了。当然你可以使用我上面的做法,并做一下适当的修改。但是不推荐这种做法


相关文章
|
8月前
|
设计模式 Java
Java设计模式【十】:装饰者模式
Java设计模式【十】:装饰者模式
54 0
装饰者模式
装饰者模式
81 0
打扮一下(装饰模式)
打扮一下(装饰模式)
71 0
|
Java
结构型模式-装饰者模式
结构型模式-装饰者模式
93 0
|
设计模式 uml
【结构型模式】二十三天学会设计模式之装饰者模式
【结构型模式】二十三天学会设计模式之装饰者模式
【结构型模式】二十三天学会设计模式之装饰者模式
|
设计模式 Java uml
Java设计模式-装饰者模式
Java设计模式-装饰者模式
138 0
Java设计模式-装饰者模式
|
缓存 Java 数据库连接
深入理解装饰者模式
深入理解装饰者模式
162 0
深入理解装饰者模式
装饰模式简单介绍
装饰模式简单介绍
107 0