设计模式-装饰模式

2023-11-18

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

在装饰模式中的各个角色有:

  • 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。   
  • 具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
  • 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
  • 具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。

模式简化

  1. 如果只有一个Concrete Component类而没有抽象的Component接口时,可以让Decorator继承Concrete Component。
  2. 如果只有一个Concrete Decorator类时,可以将Decorator和Concrete Decorator合并。

角色关系UML如下:

这里写图片描述

若没有抽象构件的存在,uml图如下:
这里写图片描述

若具体装饰只有一个.uml图如下:
这里写图片描述

装饰模式的特点

(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的引用(reference)
(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。

适用性

以下情况使用Decorator模式
1. 需要扩展一个类的功能,或给一个类添加附加职责。
2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

优点

  1. Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。
  2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

缺点

  1. 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
  2. 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
  3. 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。

设计原则

  1. 多用组合,少用继承。
    利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。
  2. 类应设计的对扩展开放,对修改关闭。

装饰者与适配者模式的区别

1.关于新职责:适配器也可以在转换时增加新的职责,但主要目的不在此。装饰者模式主要是给被装饰者增加新职责的。
2.关于原接口:适配器模式是用新接口来调用原接口,原接口对新系统是不可见或者说不可用的。装饰者模式原封不动的使用原接口,系统对装饰的对象也通过原接口来完成使用。(增加新接口的装饰者模式可以认为是其变种–“半透明”装饰者)
3.关于其包裹的对象:适配器是知道被适配者的详细情况的(就是那个类或那个接口)。装饰者只知道其接口是什么,至于其具体类型(是基类还是其他派生类)只有在运行期间才知道。[1]

实际使用

java IO 流是典型的装饰模式。

生日礼物的装饰

UML图如下:

这里写图片描述

java代码:

抽象构件:

package demo3;

/**
 * 
 * @ClassName: Gift
 * @Description: 抽象礼物
 * @author cheng
 * @date 2017-8-10 下午07:45:45
 */
public abstract class Gift {

    /**
     * 
     * @Title: packaging
     * @Description:礼物包装
     */
    public abstract void packaging();
}

具体构件:

package demo3;

/**
 * 
 * @ClassName: BirthdayGift
 * @Description: 生日礼物
 * @author cheng
 * @date 2017-8-10 下午07:47:40
 */
public class BirthdayGift extends Gift {

    /**
     * 重写
     */
    @Override
    public void packaging() {
        System.out.println("包装盒");
    }

}

装饰:

package demo3;

/**
 * 
 * @ClassName: GiftPackaging
 * @Description: 抽象装饰
 * @author cheng
 * @date 2017-8-10 下午07:54:02
 */
public class GiftPackaging extends Gift {
    // 持有抽象礼物引用
    private Gift gift;

    /**
     * 构造函数
     * 
     * @param gift
     */
    public GiftPackaging(Gift gift) {
        this.gift = gift;
    }

    /**
     * 重写
     */
    @Override
    public void packaging() {
        gift.packaging();
    }

}

具体装饰:

package demo3;

/**
 * 
 * @ClassName: SimplePackaging
 * @Description: 简单包装
 * @author cheng
 * @date 2017-8-10 下午08:03:07
 */
public class SimplePackaging extends GiftPackaging {

    /**
     * 构造函数
     * @param gift
     */
    public SimplePackaging(Gift gift) {
        super(gift);
    }

    /**
     * 重写
     */
    @Override
    public void packaging() {
        System.out.println("===============简单包装===============");
        super.packaging();
        addColorSheet();
    }

    /**
     * 
     * @Title: addColorSheet
     * @Description:私有方法
     */
    private void addColorSheet() {
        System.out.println("包彩纸");
    }

}
package demo3;

/**
 * 
 * @ClassName: LuxuryPackaging
 * @Description: 奢华包装
 * @author cheng
 * @date 2017-8-10 下午08:04:07
 */
public class LuxuryPackaging extends GiftPackaging {

    /**
     * 构造函数
     * 
     * @param gift
     */
    public LuxuryPackaging(Gift gift) {
        super(gift);
    }

    /**
     * 重写
     */
    @Override
    public void packaging() {
        System.out.println("===============奢华包装===============");
        super.packaging();
        addColorSheet();
        addCard();
        addGiftBox();
    }

    /**
     * 
     * @Title: addColorSheet
     * @Description:私有方法
     */
    private void addColorSheet() {
        System.out.println("包彩纸");
    }

    /**
     * 
     * @Title: addCard
     * @Description:私有方法
     */
    private void addCard() {
        System.out.println("加贺卡");
    }

    /**
     * 
     * @Title: addGiftBox
     * @Description:私有方法
     */
    private void addGiftBox() {
        System.out.println("加礼盒");
    }

}

测试:

package demo3;

/**
 * 
 * @ClassName: ClientTest
 * @Description:测试 
 * @author cheng
 * @date 2017-8-10 下午08:05:42
 */
public class ClientTest {

    public static void main(String[] args){
        Gift gift1 = new BirthdayGift();
        GiftPackaging giftPackaging1 = new SimplePackaging(gift1);
        giftPackaging1.packaging();

        Gift gift2 = new BirthdayGift();
        GiftPackaging giftPackaging2 = new LuxuryPackaging(gift2);
        giftPackaging2.packaging();
    }
}

运行结果:
这里写图片描述

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

设计模式-装饰模式 的相关文章

  • 设计模式的 C++ 实现---原型模式

    前文回顾 单例模式 一 单例模式 二 观察者模式 简单工厂模式 工厂方法模式 一 工厂方法模式 二 抽象工厂模式 一 抽象工厂模式 二 前言 原型模式指直接用一个已经存在的对象来创建一个新的对象 然后对新对象进行稍微修改即可 类似复制的操作
  • 常用设计模式总结

    设计模式的相关知识 很多书籍和博客中都有详细总结 本文总结的目的 1 将自己学习到的设计模式的知识按照自己的逻辑重新总结 方便查看和记忆 2 方便让自己对设计模式中常用的知识有一个系统的认知 设计模式 话设计模式 书中提到 24 种设计模式
  • 设计模式之(三)---工厂方法模式

    女娲补天的故事大家都听过吧 这个故事是说 女娲在补了天后 下到凡间一看 哇塞 风景太优美了 天空是湛 蓝的 水是清澈的 空气是清新的 太美丽了 然后就待时间长了就有点寂寞了 没有动物 这些看的到 都是静态的东西呀 怎么办 别忘了是神仙呀 没
  • Java设计模式:装饰者模式(Decorator Pattern)

    装饰者模式 涉及的重要设计原则 类应该对扩展开放 对修改关闭 装饰者模式定义 装饰者模式动态地将责任附加到对象上 若要扩展功能 装饰者提供了比继承更有弹性的替代方案 UML类图 装饰者模式事例 咖啡店 咖啡种类 1 深焙咖啡 DarkRoa
  • 面向过程和面向对象的语言有哪些,以及优缺点(一篇文章让你理解)

    C语言是面向过程的 而C python java是面向对象的 面向过程的编程思想将一个功能分解为一 个一个小的步骤 我们通过完成一个一 个的小的步骤来完成一个程序 优点 这种编程方式 符合我们人类的思维 编写起来相对比较简单 缺点 但是这种
  • 计算资源合并模式——云计算架构常用设计模式

    背景 云计算的解决方案中 最初设计可能有意遵循关注点分离的设计原则 把操作分解为独立的计算单元以便可以单独托管和部署 然而 虽然这种策略可以帮助简化解决方案的逻辑实现 但是在同一个应用程序中要部署大量的计算单元 这会增加运行时的托管成本 并
  • 设计模式——简单工厂模式

    简单工厂模式定义为 简单工厂模式又称为静态工厂方法模型 它属于类创建型模式 在简单工厂模式中 可以根据参数的不同返回不同类的实例 简单工厂专门定义一个类来负责创建其他类的实例 被创建的实例通常都具有共同的父类 简单工厂模式结构图 简单工厂模
  • 程杰“大话设计模式”中的设计原则

    单一职责原则 SRP 就一个类而言 应该仅有一个引起它变化的原因 如果一个类承担的职责过多 就等于把这些职责耦合在了一起 一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力 这种耦合会导致脆弱的设计 当发生变化时 设计会遭受到意想不到
  • 设计模式--Abstract server模式 VS Adapter 模式

    适配器类似于现实世界里面的插头 通过适配器 我们可以将分属于不同类的两种不同类型的数据整合起来 而不必去根据某一需要增加或者修改类里面的方法 Adapter mode和Proxymode的区别 Proxy的关注点是职能转移 通过引入Prox
  • 分享几个项目中用到的设计模式

    前言 之前项目中出于扩展性和有雅性的考虑 使用了多种设计模式进行项目框架的设计 主要的一些设计模式是单例模式 工厂模式 策略模式 责任链模式 代理模式这几种 现在依次讲讲这几个的主要是实现方式和在我们项目中的应用场景 核心设计模式分享 单例
  • 设计模式--提供者模式provider

    设计模式 C 提供者模式 Provider Pattern 介绍 为一个API进行定义和实现的分离 示例 有一个Message实体类 对它的操作有Insert 和Get 方法 持久化数据在SqlServer数据库中或Xml文件里 根据配置文
  • HeadFirst 设计模式学习笔记10——MVC分析

    1 M V C Model View Controller 模式 视图 控制器 这是一种范型 模型对象正是应用系统存在的理由 你设计的对象 包含了数据 逻辑和其他在你的应用领域创建定制的类 视图通常是控件 用来显示和编辑 控制器位于二者中间
  • 设计模式学习笔记-工厂模式

    设计模式学习笔记 工厂模式 作用 实现了创建者和调用者的分离 详细分类 简单工厂模式 用来生产同一等级结构中的任意产品 对于增加新的产品 必须要扩展已有的代码 工厂方法模式 用来生产同一等级结构中的固定产品 支持增加任意产品 抽象工厂模式
  • [C++]外观模式

    外观模式 Facade Pattern 隐藏系统的复杂性 并向客户端提供了一个客户端可以访问系统的接口 这种类型的设计模式属于结构型模式 它向现有的系统添加一个接口 来隐藏系统的复杂性 这种模式涉及到一个单一的类 该类提供了客户端请求的简化
  • [设计模式]模板方法模式(Template Method)

    1 意图 定义一个操作中的算法的骨架 而将一些步骤延迟到子类中 TemplateMethod使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤 2 动机 其实就是如意图所描述的 算法的骨架是一样的 就是有些特殊步骤不一样 就可以
  • 设计模式之访问者模式

    访问者模式 把被操作的对象作为元素 可变可拓展的操作作为访问者 可以说访问者中有很多操作 然后访问者访问元素 对该元素进行操作 不同的访问者有不同的操作 案例 定义访问者接口 public interface UniversalVisito
  • 设计模式-享元模式

    一 概念 如果在一个系统中存在多个相同的对象 那么只需要共享一份对象的拷贝 而不必为每一次使用都创建新的对象 目的是提高系统性能 上面的概念乍一听好像单例模式其实不是 单例模式只保存一个对象 但是这里可以有很多个不同对象 但是每个对象只有一
  • 哈工大2020软件构造Lab3实验报告

    本项目于4 21日实验课验收 更新完成 如果有所参考 请点点关注 点点赞GitHub Follow一下谢谢 2020春计算机学院 软件构造 课程Lab3实验报告 Software Construction 2020 Spring Lab 3
  • Java设计模式:模板方法模式

    作者主页 欢迎来到我的技术博客 个人介绍 大家好 本人热衷于 Java后端开发 欢迎来交流学习哦 如果文章对您有帮助 记得 关注 点赞 收藏 评论 您的支持将是我创作的动力 让我们一起加油进步吧 文章目录 一 模板方法模式的定义 二 模板方
  • 【设计模式之美】理论一:怎么才算是单一原则、如何取舍单一原则

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

随机推荐

  • html5期末知识点归纳总结,web期末考试知识点

    题型及知识点 一 知识点 上课内容全覆盖 除补充的html5和css3的内容 常用的html标记及属性 弄清楚哪些是块元素 哪些是行内元素 特殊字符标记 p40 Css属性 i 字体 font size font family font w
  • 蓝桥杯JAVA B组 2020(1)第五题 排序

    一 题目描述 小蓝最近学习了一些排序算法 其中冒泡排序让他印象深刻 在冒泡排序中 每次只能交换相邻的两个元素 小蓝发现 如果对一个字符串中的字符排序 只允许交换相邻的两个字符 则在所有可能的排序方案中 冒泡排序的总交换次数是最少的 例如 对
  • SQLCipher核心思想

    加密原理 page data iv hmac iv是一段随机数 可以保证每一页的iv值都不一样 和page data一起作用 用于生成hmac值 sizeof page data p
  • kubeasz 二进制安装k8s高可用集群

    一 kubeasz介绍 项目致力于提供快速部署高可用k8s集群的工具 同时也努力成为k8s实践 使用的参考书 基于二进制方式部署和利用ansible playbook实现自动化 既提供一键安装脚本 也可以根据安装指南分步执行安装各个组件 二
  • Maven2部署构件到Nexus时出现的Failed to transfer file错误

    原文出处 http www javatang com archives 2010 01 23 4518375 html 作者 Jet Mah from Java堂 声明 可以非商业性任意转载 转载时请务必以超链接形式标明文章原始出处 作者信
  • jni基础

    JNI相关 静态注册 java代码需要和C 代码相符通讯就需要通过JNI来进行注册 java public native String stringFromJNI 代表该函数的实现在so so Java com first firstndk
  • 内存管理方案

    内存管理方案 Memory Management System Author Owen 目 录 内存管理方案 1 目 录 1 1 概述 2 2 理论依据 2 2 1 不对内存进行管理 2 2 2 对内存进行内部管理 2 3 实现方案 2 3
  • RMI-Remote Method Invocation远程方法调用的使用

    手动启动服务方式 参考自 https www cnblogs com fanghao p 8918953 html 接口 import java rmi Remote import java rmi RemoteException publ
  • selenium二次封装

    使用到相关技术 selenium testng log4j maven selenium主要生成driver 获取页面元素等 testng主要Run程序 生成测试报告 管理用例执行顺序 断言等 log4j主要用来打印log日志 maven主
  • RuntimeError: CUDA unknown error - this may be due to an incorrectly set up environment

    解决方案 输入指令sudo shutdown r now即可重新启动驱动 如果还是无法解决则需要重新安装驱动
  • 【系统】C/C++内存管理之内存分配

    文章目录 0 内存分配方式 从静态存储区域分配 在栈上创建 在堆上分配 程序内存空间 1 C语言内存分配方式 静态与动态内存分配区别 1 1 静态分配方式 1 2 动态分配方式 1 malloc函数 2 calloc 函数 3 reallo
  • java.sql.SQLException: Incorrect string value: '\xF0\x9F\x92\x94' for column 'content' at row 1

    使用Mysql服务器的utf8字符编码 在存入移动端emoji表情时会报异常 Caused by java sql SQLException Incorrect string value xF0 x9F x98 x84 for column
  • 深度学习:实现mnist手写数字识别

    本文为 365天深度学习训练营 中的学习记录博客 原作者 K同学啊 接辅导 项目定制 我的环境 1 语言环境 Python 3 7 2 编译器 Pycharm 3 深度学习环境 TensorFlow2 5 一 前期工作 1 设置GPU 若使
  • Ubuntu20.04开机后,弹出检测到系统程序出现问题解决方法

    打开配置文件 sudo gedit etc default apport 然后 将其中的enable选项改成0 即可解决此问题
  • C++ *,&

    文章目录 语法 1 取地址运算符 2 间接寻址运算符 示例 1 可以累计使用间接寻址运算符来取消引用指向指针的指针 2 静态成员的地址 3 引用类型的地址 4 函数地址作为参数 4 示例 references 语法 1 取地址运算符 又称
  • c#量化交易_我用1天时间搭建自主量化交易(程序化交易)平台

    VirtualApi目前支持上海期货交易所的CTP回测 http www virtualapi cn 实盘期货 支持CTP http www kaihucn cn Simnow 上期CTP接口官方网站和模拟账户注册 http www sim
  • 【KITTI】KITTI数据集简介(一) — 激光雷达数据

    本文为博主原创文章 未经博主允许不得转载 本文为专栏 python三维点云从基础到深度学习 系列文章 地址为 https blog csdn net suiyingy article details 124017716 KITTI数据集的详
  • C++基础——new和delete动态开辟

    目录 前言 一 new关键字 格式2 动态开辟多个数据的堆区空间 数组 总结 二 自定义类型的开辟 1 区别 2 匹配错误讲解 错误示范例1 delete 的执行原理 错误示范2 三 malloc new失败的区别 1 malloc失败 2
  • 如何在WPS、MathType中输入傅立叶变换的符号

    这里写自定义目录标题 在MathType中 先输入大写F 选择上 然后在 mathtype 的 style Other 选择 Palace Script MT字体 如果没有该字体 选择Lucida Calligraphy字体 如果使用的是E
  • 设计模式-装饰模式

    装饰模式指的是在不必改变原类文件和使用继承的情况下 动态地扩展一个对象的功能 它是通过创建一个包装对象 也就是装饰来包裹真实的对象 在装饰模式中的各个角色有 抽象构件 Component 角色 给出一个抽象接口 以规范准备接收附加责任的对象