DECORATOR 装饰模式

2023-10-27

DECORATOR  装饰模式   对象结构性模式


1、意图

动态地给一个对象增加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。


2、别名

包装器Wrapper


3、动机

        有时我们希望给某对象而不是整个类添加一些功能。例如,一个图形用户界面工具箱允许你对任意一个用户界面组件添加一些特性,例如边框,或是一些行为,例如窗口滚动。

        使用继承机制是添加功能的一种有效途径,从其它类继承过来的边框特性可以被多个子类的实例所使用。但这种方法不够灵活,因为边框的选择是静态的,用户不能控制对组件加边框的方式和时机。

        一种较为灵活的方式是将组件嵌入另一个对象中,由这个对象添加边框。我们成这个嵌入的对象为装饰。这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。它将客户请求转发给该组件,并且可能在转发前后执行一些额外的动作(例如画一个边框)。透明性使得你可以递归的嵌套多个装饰,从而可以添加任意多的功能,如下图所示。

        例如,假定有一个对象TextView,它可以在窗口中显示正文。缺省的TextView没有滚动条,因为我们可能有时并不需要滚动条。当需要滚动条时,我们可以用ScrollDecorator添加滚动条。如果我们还想在TextView周围添加一个粗黑边框,我们可以使用BorderDecorator添加。因此只要简单地将这些装饰和TextView进行组合,就可以达到预期的效果。

        下面的对象图展示了如何将一个TextView对象与BorderDecorator以及ScrollDecorator对象组装起来产生一个具有边框和滚动条的文本显示窗口。

        ScrollDecorator和BorderDecorator类是Decorator类的子类。Decorator类是一个可视组件的抽象类,用于装饰其它可视组件,如下图所示。


        VisualComponent是一个描述可视对象的抽象类,它定义了绘制和事件处理的接口。注意Decorator类怎样将绘制请求简单地发送给它的组件,以及Decorator的子类如何扩展这个操作。

        Decorator的子类为特定功能可以自由地添加一些操作。例如,如果其他对象知道界面中恰好有一个ScrollDecorator对象,这些对象就可以用ScrollDecorator对象的ScrollTo操作滚动这个界面。这个模式中有一点很重要,它使得在VisualComponent可以出现的任何地方都可以有装饰。因此,客户通常不会感觉到装饰过的组件与未装饰组件之间的差异,也不会与装饰产生任何依赖关系。


4、适用性

        以下情况使用Decorator模式

        ·  在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

        · 处理那些可以撤销的职责。

        · 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。


5、结构


6、参与者

        · Component(VisualComponent)

          -- 定义一个对象接口,可以给这些对象动态地添加职责。

        · ConcreteComponent(TextView)

          -- 定义一个对象,可以给这个对象添加一些职责。

        · Decorator

          -- 维持一个指向Component对象的指针,并定义一个与Component接口一致的接口。

        · ConcreteDecorator(BorderDecorator,ScrollDecorator)

          --向组件添加职责。


7、协作

        Decorator将请求转发给它的Component对象,并有可能在转发请求前后执行一些附加的动作。


8、效果

        Decorator模式至少有两个主要优点和两个缺点:

        1)比静态继承更灵活    与对象的静态继承(多重继承)相比,Decorator模式提供了更加灵活的向对象添加职责的方式。可以用添加和分离的方法,用装饰在运行时刻增加和删除职责。相比之下,继承机制要求为每个添加的职责创建一个新的子类(例如,BorderScrollableTextView,BorderedTextView)。这会产生许多新的类,并且会增加系统的复杂度。此外,为一个特定的Component类提供多个不同的Decorator类,这就使得你可以对一些职责进行混合和匹配。

          使用Decorator模式可以很容易地重复添加一个特性,例如在TextView上添加双边框时,仅需将添加两个BorderDecorator即可。而两次继承Border类则极容易出错。

        2)避免在层次结构高层的类有太多的特征    Decorator模式提供了一种“即用即付”的方法来添加职责。它并不师徒在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以定义一个简单的类,并且用Decorator类给它逐渐地添加功能。可以从简单的部件组合出复杂的功能。这样,应用程序不必为不需要的特征付出代价。同时也更易于不依赖于Decorator所扩展(甚至是不可预知的扩展)的类而独立地定义新类型的Decorator。扩展一个复杂类的时候,很可能会暴露与添加的职责无关的细节。

        3)Decorator与它的Component不一样    Decorator是一个透明的包装。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰时不应该依赖对象标识。

        4)有许多小对象    采用Decorator模式进行系统设计往往会产生许多看上去很相似的小对象,这些对象仅仅在他们相互连接的方式上有所不同,而不是他们的类或是他们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对他们进行定制,但是很难学习这些系统,排错也很困难。


9、实现

        使用Decorator模式时应注意以下几点:

        1)接口的一致性    装饰对象的接口必须与它所装饰的Component的接口是一致的,因此,所有的ConcreteDecorator类必须有一个公共的父类(至少在C++中如此)。

        2)省略抽象的Decorator类    当你仅需要添加一个职责时,没有必要定义抽象Decorator类。你常常需要处理现存的类层次结构而不是设计一个新系统,这时你可以把Decorator向Component转发请求的职责合并到ConcreteDecorator中。

        3)保持Component类的简单性    为了保证接口的一致性,组件和装饰必须有一个公共的Component父类。因此保持这个类的简单性是很重要的;即,它应集中于定义接口而不是存储数据。对数据表示的定义应延迟到子类中,否则Component类会变得过于复杂和庞大,因而难以大量使用。赋予Component太多的功能也使得具体的子类有一些他们并不需要的功能的可能性大大增加。

        4)改变对象外壳与改变对象内核    我们可以将Decorator看做一个对象的外壳,它可以改变这个对象的行为。另外一种方法是改变对象的内核。例如,Strategy模式就是一个用于改变内核的很好的模式。

          当Component类原本就很庞大时,使用Decorator模式代价太高,Strategy模式相对更好一些。在Strategy模式中,组件将它的一些行为转发给一个独立的策略对象,我们可以替换strategy对象,从而改变或扩充组件的功能。

          例如我们可以将组件绘制边界的功能延迟到一个独立的Border对象中,这样就可以支持不同的边界风格。这个Border对象是一个Strategy对象,它封装了边界绘制策略。我们可以将策略的数目从一个扩充为任意多个,这样产生的效果与对装饰进行递归嵌套是一样的。

          由于Decorator模式仅从外部改变组件,因此组件无需对它的装饰有所了解;也就是说,这些装饰对该组件是透明的,如下图所示:


          在Strategy模式中,component组件本身知道可能进行那些扩充,因此它必须引用并维护相应的策略,如下图所示:


          基于Strategy的方法可能需要修改component组件以适应新的扩充。另一方面,一个策略可以有自己特定的借口,而装饰的接口则必须与组件的接口一致。例如,一个绘制边框的策略仅需要定义生成边框的接口(DrawBorder,GetWidth等),这意味着记事Component类很庞大时,策略也可以很小。


10、代码示例

        网络上代码示例很多,可随意参考。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

DECORATOR 装饰模式 的相关文章

  • 如何创建一个能够包装实例、类和静态方法的Python类装饰器?

    我想创建一个Python类装饰器 这将能够无缝包装类可能具有的所有方法类型 实例 类和静态 这是我现在的代码 其中破坏它的部分已注释 def wrapItUp method def wrapped args kwargs print Thi
  • 【设计模式之美】理论一:怎么才算是单一原则、如何取舍单一原则

    文章目录 一 如何判断类的职责是否足够单一 二 类的职责是否设计得越单一越好 开始学习一些经典的设计原则 其中包括 SOLID KISS YAGNI DRY LOD 等 本文主要学习单一职责原则的相关内容 单一职责原则的定义 一个类只负责完
  • timeit.timeit 方法的装饰器?

    我正在尝试编写一个简单的时间装饰器来测量函数所花费的时间 然而 下面的代码给出了我们的递归错误 它出什么问题了 import timeit def measure func def wrapper func name func name s
  • python 装饰器中的 self

    我想要一个装饰器将装饰函数添加到列表中 如下所示 class My Class object def init self self list decorator def my function self print Hi 我希望将 my f
  • 系列一、 单例设计模式

    一 单例设计模式 1 1 概述 单例模式 Singleton Pattern 是Java中最简单的设计模式之一 这种类型的设计模式属于创建者模式 它提供了一种创建对象的最佳方式 这种模式涉及到一个单一的类 该类负责创建自己的对象 同时确保只
  • C++设计模式 --1.工厂模式和单例模式

    文章目录 1 工厂模式 简单工厂模式 工厂方法模式 抽象工厂模式 2 单例模式 懒汉式 饿汉式 1 工厂模式 简单工厂模式
  • python中具有相同名称的对象引用不同的id

    在下面的代码片段中 两个对象名为div在第 1 行和第 2 行创建 python如何区分两者div在同一作用域下创建的对象 When id 应用于两个对象 对于相似的命名对象会显示两个不同的地址 为什么会这样呢 def div a b re
  • 自制方法装饰器删除所有元数据,我该如何解决它?

    我使用 NestJS 和 Nestjs swagger 模块来获取简单的 api 文档 但我遇到了麻烦 因为我需要验证服务的响应 这就是我创建自制方法装饰器的原因 但是当我使用它时 我遇到了很大的麻烦 来自另一个装饰器的所有元数据都丢失了
  • 为泛型接口配置装饰器,并在简单注入器中将所有实例注入到具有非泛型接口参数的构造函数

    我一直在使用与所描述的非常相似的模式在这篇优秀的文章中 http www cuttingedge it blogs steven pivot entry php id 91将命令和查询作为对象 我还使用 SimpleInjector 作为
  • 记住一个函数,以便当我在 Python 中重新运行该文件时它不会被重置

    我经常在 Python 中进行交互式工作 其中涉及一些我不想经常重复的昂贵操作 我通常运行我经常处理的任何 Python 文件 如果我写 import functools32 functools32 lru cache def square
  • @classmethod 的位置

    装饰器类方法的源代码位于python源代码中的哪里 具体来说 我无法找到它在版本 2 7 2 中定义的确切文件 我没有回答你的问题 但下面的代码显示了装饰器可能相当于classmethod 用纯 Python 编写 因为源代码中的那个是用
  • 默认参数装饰器 python

    Python 3 6 我正在尝试创建一个装饰器 自动将参数的字符串指定为默认值 such as def example one one two two three three pass 相当于 DefaultArguments def ex
  • 为什么 Python 装饰器不能跨定义链接?

    为什么以下两个脚本不等效 摘自另一个问题 了解 Python 装饰器 https stackoverflow com questions 739654 understanding python decorators def makebold
  • python中装饰器类的解释

    在阅读一些 python 模块时 我遇到了这个装饰器类 this decorator lets me use methods as both static and instance methods class omnimethod obje
  • 如何使用和应用 JavaScript 装饰器?

    我试图了解如何在一段非常简单的代码中使用装饰器 这样我就可以将这个概念应用到我更大的项目中 从 Addy Osmani 的文章中得到启发here https medium com google developers exploring es
  • 如何使用 python-decorator 包来装饰类方法?

    我有一个装饰器 我想用它来装饰类方法 在下面的示例中 mydec 装饰器本身可以正常工作 但是在使用 help 或 pydoc 时它不会保留函数签名 为了解决这个问题 我研究了使用 decorator python decorator 包
  • 代理模式和装饰者模式的区别

    你能给出任何好的解释吗 Proxy and 装饰者 我看到的主要区别是 当我们假设Proxy uses 作品 and 装饰者 uses 聚合那么似乎很清楚 通过使用多个 一个或多个 装饰器您可以修改 添加功能到预先存在的实例 装饰 而Pro
  • Python - 装饰器 - 尝试访问方法的父类

    这不起作用 def register method name None def decorator method The next line assumes the decorated method is bound which of co
  • 使用 ASP.NET MVC Linq To SQL 进行验证:如何避免编辑生成的源?

    我正在阅读一些关于验证的文档ASP NET MVC 忽略那些建议重新发明轮子编写自己的验证逻辑的人 大多数文章提倡使用xVal或数据注释验证器 两者都允许通过装饰模型的属性 进行声明性验证 我想我会去xVal 因为它似乎是最受建议的 因此
  • Typescript,装饰异步函数

    我正在尝试用一些异步函数 2 来装饰异步函数 1 E g function func2 param return target any propertyKey string descriptor PropertyDescriptor gt

随机推荐

  • java concurrent 探秘

    java concurrent 探秘 转 我们都知道 在JDK1 5之前 Java中要进行业务并发时 通常需要有程序员独立完成代码实现 当然也有一些开源的框架提供了这些功能 但是这些依然没有JDK自带的功能使用起来方便 而当针对高质量Jav
  • Python爬虫笔记——post请求、cookies及session

    一 post请求 post和get都可以带着参数请求 不过get请求的参数会在url上显示出来 但post请求的参数就不会直接显示 而是隐藏起来 像账号密码这种私密的信息 就应该用post的请求 如果用get请求的话 账号密码全部会显示在网
  • Python中的小工具和setWidget等相关概念解析

    Python中的小工具和setWidget等相关概念解析 在Python编程中 无论是桌面应用程序还是Web应用程序 我们都需要用到各种小工具来构建UI界面 其中 Qt是一种流行的GUI框架 提供了很多用于创建UI界面的小部件 widget
  • 每日一题 102二叉树的层序遍历

    题目 给你二叉树的根节点 root 返回其节点值的 层序遍历 即逐层地 从左到右访问所有节点 示例 1 输入 root 3 9 20 null null 15 7 输出 3 9 20 15 7 示例 2 输入 root 1 输出 1 示例
  • 4.3 C++成员模板参数、显示实例化、特化

    C 成员模板参数 关键字 typename 内嵌是指定义在类的定义中的 依赖是指依赖于一个模板参数O typedef类型名是指这里最终要指出的是个类型名 using给模板起别名 显示实例化和隐式实例化 模板的全特化与偏特化 因为编译器认为
  • python-parser.parse_args()解析参数

    import argparse 设置参数对象 简要设置为该程序要执行什么任务 parser argparse ArgumentParser mission name 调用 add argument 方法添加参数 parser add arg
  • 自举电路可以增加输入阻抗,你知道吗?

    原文来自微信公众号 工程师看海 以前写过一篇文章 介绍自举电路在BUCK电源的应用 驱动高边MOS https www dianyuan com eestar article 2127 html 反馈不错 今天再来介绍下自举电路增加输入阻抗
  • nodejs 下使用js封装console.log方法输出自定义的彩色字体,遇到Octal escape sequences are not allowed in template strings

    阳光明媚 在家敲javascript的代码 控制台里清一色的黑底白字 我console log出来的文字也是黑底白字淹没在茫茫字母海中 所以我想封装一下log方法 能自己定义字体还有背景的颜色 然后就出现了这个报错 Octal escape
  • OpenCV 图像旋转

    1 RotateArbitrarilyAngle Mat src Mat dst float angle 2 3 float radian float angle 180 0 CV PI 4 5 填充图像 6 int maxBorder i
  • SQL 题目(一)

    url http www cnblogs com finejob articles 974900 html 其他题目 url 为管理岗位业务培训信息 建立3个表 S S SN SD SA S SN SD SA 分别代表学号 学员姓名 所属单
  • 【轩说AI】无监督特征学习——主成分分析、稀疏编码、自编码器

    文章目录 无监督学习 无监督特征学习 主成分分析 稀疏编码 自编码器 主成分分析PCA 理解什么是PCA 有什么用处 稀疏编码 编码 交替优化 自编码器 Auto Encoder 稀疏自编码器 无监督学习 无监督特征学习 主成分分析 稀疏编
  • FortiGate下用户访问特定网址不通

    1 现状 如图 上网行为管理和防火墙控制用户的数据访问 到外网的出口链路有2条 联通的和电信的 其中联通的优先级较高 2 现象 用户访问www xxxxbbs com不通 3 分析 在上网行为管理上做源和目前地址排除 发现仍不能访问排除了上
  • ERP为什么在生产制造环节应用效果不理想?

    ERP在生产制造环节应用效果不理想的根本原因是ERP负责的是宏观管控 而生产制造环节应该使用MES来进行精细化管理 下面就详细讨论下 生产制造环节发生在工厂 工厂可以没有ERP 但如果要用系统 必定是MES系统 ERP是为企业服务的 MES
  • 学习汇编语言MASM笔记1

    关于源程序书写格式 太基础的略去 只记录一些我还不熟悉的内容 参考https wenku baidu com view 7ca70e0ef342336c1eb91a37f111f18582d00c5d html 其他网页的参考以挂上相关连接
  • Flutter 踩坑TabBar之TabController.addListener

    一 问题 之前提到TabBar的第二种实现方式 TabController是有坑的 问题 这个监听在点击切换tab的时候会回调两次 左右滑动切换tab正常调用一次 二 原因 点击切换tab的时候执行了一个动画效果 滑动切换的时候是没有的 在
  • 用JAVA写的一个自动化简单示例!

    public class lianxi public static void main String args throws InterruptedException System getProperty webDriver firefox
  • 2022-03-24 RK3566 MIPI屏 调试记录,panel-init-sequence 命令格式介绍

    一 Single channel mipi屏的硬件连接如下 二 根据屏厂给的初始化代码配置dts了 屏厂给的初始化代码分几种形式 这个文章里面RK PX30 android8 1的mipi屏调试 要不要买菜 的博客 CSDN博客 有比较详细
  • Ubuntu20.04(linux)离线安装nacos

    一 解压 1 首先local目录得有nacos server 2 0 4 tar gz包 切换目录cd usr local执行以下命令 tar zxvf nacos server 2 0 4 tar gz 二 创建nacos service
  • postgresql安装及常见错误处理

    postgresql安装及常见错误处理 一 安装说明 最近 由于业务需要 我抛弃了一直使用的mysql数据库 开始转向postgresql数据库 至于为什么选择postgresql 肯定是postgresql比mysql更强大 对于post
  • DECORATOR 装饰模式

    DECORATOR 装饰模式 对象结构性模式 1 意图 动态地给一个对象增加一些额外的职责 就增加功能来说 Decorator模式相比生成子类更为灵活 2 别名 包装器Wrapper 3 动机 有时我们希望给某对象而不是整个类添加一些功能