装饰者模式详解

简介: 装饰者模式详解

装饰者模式


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


问题


看一个饮料喝调料的问题


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


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


要点


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

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

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

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


相关文章
|
存储 监控 安全
推荐5款极具效率的实用工具软件
每次分享实用的软件,都会给人一种踏实和喜悦的感觉,这也是我热衷于搜集和推荐高效工具软件的原因。
347 1
|
存储 编解码 安全
Opus从入门到精通(二):编解码器使用
opus_encoder_get_size()返回编码器状态要求的大小。注意,这段代码的未来版本可能改变大小,所以没有assuptions应该对它做出。编码器状态在内存中总是连续,复制它只要一个浅拷贝就足够了。使用opus_encoder_ctl()接口可以改变一些编码器的参数设置。所有这些参数都已有缺省值,所以只在必要的情况下改变它们。
1914 0
|
5月前
|
关系型数据库 Java MySQL
【设计模式】【结构型模式】桥接模式(Bridge)
一、入门 什么是桥接模式? 桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是将抽象与实现分离,让它们可以独立变化。简单来说,它像一座“桥”连接了两个维度的变化,避免用继承导致代
315 10
|
机器学习/深度学习 算法 Java
通过 Java Vector API 利用 SIMD 的强大功能
通过 Java Vector API 利用 SIMD 的强大功能
527 10
|
数据可视化 开发者 索引
详解Wireshark LUA插件函数:function p_myproto.dissector(buffer, pinfo, tree)
在 Wireshark 中,LUA 插件通过 `function p_myproto.dissector(buffer, pinfo, tree)` 扩展协议解析能力,解析自定义应用层协议。参数 `buffer` 是 `PacketBuffer` 类型,表示原始数据包内容;`pinfo` 是 `ProtoInfo` 类型,包含数据包元信息(如 IP 地址、协议类型等);`tree` 是
505 1
|
搜索推荐 Java Go
深入了解归并排序算法
深入了解归并排序算法
214 0
|
Java 测试技术 数据库连接
@Before 和 @BeforeClass 注释的区别
【8月更文挑战第22天】
792 0
|
IDE Java 应用服务中间件
【Java】已解决java.lang.ClassNotFoundException异常
【Java】已解决java.lang.ClassNotFoundException异常
4031 0
|
Linux
linux系统中使用QT实现CAN通信的方法
linux系统中使用QT实现CAN通信的方法
1491 0
|
数据采集 传感器 XML
Landsat Collection 2 数据集详细介绍(T1/T2产品差异)
Landsat Collection 2 数据集详细介绍(T1/T2产品差异)
797 0
Landsat Collection 2 数据集详细介绍(T1/T2产品差异)