十七 结构型-装饰模式(Decorate)

293 阅读3分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

image.png 其他设计模式介绍
创建型: 工厂方法 抽象工厂 原型
结构型: 适配器 桥接模式 组合模式 装饰模式 外观模式 享元模式 代理模式
行为型: 职责链 命令 解释器 迭代器 中介者 备忘录 状态模式 策略模式 模板方法 访问者

定义

装饰模式*指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

UML图

image.png

示例

**下面我们看一个例子,我们以Java的IO结构的装饰者为例。 **

  1. UML图如下

image.png

FileInputStream等三个是一些主体,其中FilterInputStream是个中间层,继承一些公共的东西,而下面就是一些装饰者。

  1. 编写自己的Java I/O装饰者 -利用扩展java这个FilterInputStreamsh中间层实现自己的装饰者对象

该例子功能是把文件输入流或者其他主体输入流里面输入过来的字串转换成大写的这么一个自定义的把小写全部实现转化为大写输入流

//首先实现中间层这个对象FilterInputStream
//然后在构造函数中要把这个超类带进来,然后super进去
class UpperCaseInputStream(steam: InputStream?) : FilterInputStream(steam) {
    //下面两个就是类似cost方法,需要实现的,
    @Throws(IOException::class)
    override fun read(): Int //单字符的读
    {
        //这个super.read()就是调用上面super(in);的主题对象
        val c = super.read()
        return if (c == -1) c else c.toChar().uppercaseChar().toInt()
    }

    @Throws(IOException::class)
    override fun read(b: ByteArray, offset: Int, len: Int): Int {
        //多字符的读
        val result = super.read(b, offset, len)
        for (i in 0 until result) {
            b[i] = b[i]
        }
        return result
    }
}

fun main() {
    var c: Int
    try {
        val steam: InputStream = UpperCaseInputStream(
            BufferedInputStream(
                FileInputStream("F:\test.txt")
            )
        )
        while (steam.read().also { c = it } >= 0) {
            print(c.toChar())
        }
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

意思就是:装饰者添加新功能,比如添加一个调料,开放就是添加一个新功能,关闭就是添加一个新功能时,对原有的代码不轻易改变。也就是说在设计一个项目体系结构的时候,对添加新功能,新代码是开放的,对已经设计好的,或者测试好的代码是不允许修改的。

优点

  • 动态扩展类功能,比类继承灵活,且对客户端透明;
  • 继承关系的一种替代方案。相比与类继承的父子关系,装饰模式 更像是一种组合关系(is-a);
  • 可以对同一个被装饰对象进行多次装饰,创建出不同行为的复合功能;

缺点

  • 多层装饰比较复杂(灵活的同时会带来复杂性的增加);
  • 装饰嵌套过多,会产生过多小对象(每个装饰层都创建一个相应的对象);
  • 装饰嵌套过多,易于出错,且调试排查比较麻烦(需要一层一层对装饰器进行排查,以确定是哪一个装饰层出错);

使用场景

  • 需要扩展一个类的功能,或给一个类增加附加功能;
  • 需要动态地给一个对象增加功能,且这些功能可以再动态地撤销;
  • 需要为一批的兄弟类进行改装或加装功能;