设计模式——装饰模式

2023-11-05

装饰模式

1.装饰模式动机及定义

1.1模式动机

        买了新房(毛胚房)需要装修,对新房进行装修并没有改变房子用于居住的本质,但它让房子变的更漂亮,更加满足居家的需求。在软件设计中,我们也可以用类似的技术对原有对象(新房)的功能进行扩展(装修),以获得更加符合用户需求的对象。这种技术在设计模式中被成为装饰模式,装饰模式可以动态地给父类增加功能

        装饰模式可以在不改变一个对象本身的基础上上给对象增加额外的新行为,在现实生活中,这种情况比比皆是,如一张照片,可以不改变照片本身,给它增加一个相框,使得它具有防潮的功能,而且用户可以根据需要给它增加不同类型的相框,甚至可以在一个小的相框的外面再套一个大相框。

        在软件外发中,类似给照片加相框的情况也随处可见。如可以给一个图形界面构件增加边框、滚动等新的特性,给一个数据加密类增加更复杂的加密算法等。

        一般有两种方式可以实现给一个类或对象增加行为:

        1.继承机制

        使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。

        2.关联机制

        关联机制是更加灵活的方法,即将一个类的对象嵌入另一个新对象中,由另一个对象类来决定是否调用嵌入对象的行为并扩展新的行为,我们称这个新的对象(即另一个对象)为装饰器(Decorator)。为了使得装饰器与它所装饰的对象对客户端来说透明,装饰器类和被装饰的类必须实现相同的接口,客户端使用时无须关心一个类的对象是否被装饰过,可以一致性地使用未被装饰的对象以及装饰好的对象。我们可以在被装饰的类中调用在装饰器类中定义的方法,实现更多复杂的功能,而且由于装饰器类和被装饰的类实现了相同的接口,已经被装饰过的对象可以继续作为新的被装饰对象进行装饰,这种透明性使得我们可以递归嵌套多个装饰,从而可以添加任意多的功能。

        装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。这就是装饰模式的动机。

1.2模式定义

        装饰模式定义(Decorator Pattern)定义:动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以成为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。

        英文定义:“Attach additional responsibilities to an object dynamically.Decorators provide a flexible alternative to subclassing for extending functionality.”

2.装饰模式结构与分析

2.1模式结构

图1 装饰模式结构图

装饰模式包含如下角色。

1.Component(抽象构件)

        抽象构件定义了对象的接口,可以给这些对象动态增加职责(方法),抽象构件是具体构件和抽象装饰类的共同父类,它声明了在具体构件中实现的业务方法,它的引入可以是客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。

2.ConcreteComponent(具体构件

        具体构件定义了具体的构件对象,实现了在抽象构件中声明的方法,装饰器模式可以给它增加额外的职责(方法)。

3.Decorator(抽象装饰类)

        抽象装饰类是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的饮用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。

4.ConcreteDecorator(具体装饰类)

        具体装饰类是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰器都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法以便扩充对象的行为。

3.装饰模式实例与解析

        题目:某图书管理系统中,书籍类(Book)具有借书方法borrowBook()和还书方法returnBook() 。现需要动态给书籍对象添加冻结方法freeze()和遗失方法lose()。使用装饰模式设计该系统,绘制类图并编程实现。

3.1实例类图

3.2实例代码及解释

1.抽象构件类ABookt(抽象书本)

 

public interface ABook {

    public void returnBook();

    public void borrowBook();

}

        ABook是抽象构件类,在其中声明了returnBook()和 borrowBook()方法,无论书本如何改变,这两个方法都必须具有,它是具体构件和装饰器共有的方法。

2.具体构件类Book(书本类)

public final class Book implements ABook{

    public Book(){

        System.out.println("这是一本书!");

    }



    @Override

    public void returnBook() {

        System.out.println("图书已归还!!");

    }



    @Override

    public void borrowBook() {

        System.out.println("图书已借阅!");



    }

}

        Book是ABook的子类,它是具体构件类,提供了returnBook()和borrowBook()方法的实现,它是一个可以被装饰的类。在这里Book被声明为final类型,意味着不能通过继承来扩展其功能,但是可以通过关联关系来扩展,也就是通过使用装饰器来装饰它。

3.抽象装饰类Changer(变化类)

public class Changer implements ABook{

    private ABook book;

    public Changer(ABook book){

        this.book = book;

    }



    @Override

    public void returnBook() {

        book.returnBook();

    }



    @Override

    public void borrowBook() {

        book.borrowBook();

    }

}

        Changer是抽象装饰类,它是所有具体装饰类的父类,同时它也是抽象构件的子类。Changer类是装饰类的核心,它定义了一个抽象构件类型的对象book,可以通过构造函数或者Setter方法来给该对象赋值,在本实例中使用的是构造函数,并且它也实现了returnBook()和borrowBook()方法,但是它通过调用book对象中的returnBook()和borrowBook()来实现,这样就可以保证原有方法不会丢失,而且可以在它的子类中增加新的方法,扩展原有对象的功能。

4.具体装饰类freezeBook(冻结书类)

public class freezeBook extends Changer{

    public freezeBook(ABook book){

        super(book);

    }



    public void freeze(){

        System.out.println("图书已冻结!!!");

    }

}

        freezeBook类是Changer的子类,它继承了在Changer中定义的方法,还增加新的方法,也就是说它即可以调用原有对象的方法,又可以对其进行补充,为其增加新的职责,如可以冻结图书。

5.具体装饰类loseBook(丢失书类)

public class loseBook extends Changer{

    public loseBook(ABook book){

        super(book);

    }



    public void lose(){

        System.out.println("图书已丢失!!!!!");

    }

}

        loseBook类也是Changer的子类,它继承类在Changer中定义的方法,还增加了新的方法lose(),实现图书丢失。

3.3辅助代码

客户端测试类Client如下:

public class Client {

    public static void main(String[] args) {

        ABook book;

        book = new Book();

        book.borrowBook();

        book.returnBook();

        System.out.println("--------------");

        freezeBook b1 = new freezeBook(book);

        b1.borrowBook();

        b1.returnBook();

        b1.freeze();

        System.out.println("--------------");

        loseBook b2 = new loseBook(book);

        b2.borrowBook();

        b2.returnBook();

        b2.lose();

    }

}

3.4结果

4.装饰模式效果与应用

4.1 装饰模式的优点

 

(1)装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。

(2)可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。

(3)通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。

(4)具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。

4.2装饰模式的缺点

(1)使用装饰器模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。

(2)这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承模式更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

4.3模式适用环境

在以下情况可以使用装饰模式。

  1. 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  2. 需要动态地给一个对象增加功能,这些功能也可以动态地撤销。
  3. 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展的维护时。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,是的子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类)。

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

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

  • 使用 LinkedList 实现下一个和上一个按钮

    这可能是一个愚蠢的问题 但我很难思考清楚 我编写了一个使用 LinkedList 来移动加载的 MIDI 乐器的方法 我想制作一个下一个和一个上一个按钮 以便每次单击该按钮时都会遍历 LinkedList 如果我硬编码itr next or
  • .properties 中的通配符

    是否存在任何方法 我可以将通配符添加到属性文件中 并且具有所有含义 例如a b c d lalalala 或为所有以结尾的内容设置一个正则表达式a b c anything 普通的 Java 属性文件无法处理这个问题 不 请记住 它实际上是
  • 如何使用assertEquals 和 Epsilon 在 JUnit 中断言两个双精度数?

    不推荐使用双打的assertEquals 我发现应该使用带有Epsilon的形式 这是因为双打不可能100 严格 但无论如何我需要比较两个双打 预期结果和实际结果 但我不知道该怎么做 目前我的测试如下 Test public void te
  • jQuery AJAX 调用 Java 方法

    使用 jQuery AJAX 我们可以调用特定的 JAVA 方法 例如从 Action 类 该 Java 方法返回的数据将用于填充一些 HTML 代码 请告诉我是否可以使用 jQuery 轻松完成此操作 就像在 DWR 中一样 此外 对于
  • 如何更改javaFX中按钮的图像?

    我正在使用javaFX 我制作了一个按钮并为此设置了图像 代码是 Image playI new Image file c Users Farhad Desktop icons play2 jpg ImageView iv1 new Ima
  • 谷歌应用程序引擎会话

    什么是java应用程序引擎 默认会话超时 如果我们将会话超时设置为非常非常长的时间 会不会产生不良影响 因为谷歌应用程序引擎会话默认情况下仅存储在数据存储中 就像facebook一样 每次访问该页面时 会话仍然永远存在 默认会话超时设置为
  • Java 公历日历更改时区

    我正在尝试设置 HOUR OF DAY 字段并更改 GregorianCalendar 日期对象的时区 GregorianCalendar date new GregorianCalendar TimeZone getTimeZone GM
  • java.lang.IllegalStateException:应用程序 PagerAdapter 更改了适配器的内容,而没有调用 PagerAdapter#notifyDataSetChanged android

    我正在尝试使用静态类将值传递给视图 而不是使用意图 因为我必须传递大量数据 有时我会收到此错误 但无法找出主要原因是什么 Error java lang IllegalStateException The application s Pag
  • Eclipse Maven Spring 项目 - 错误

    I need help with an error which make me crazy I started to study Java EE and I am going through tutorial on youtube Ever
  • 帮助将图像从 Servlet 获取到 JSP 页面 [重复]

    这个问题在这里已经有答案了 我目前必须生成一个显示字符串文本的图像 我需要在 Servlet 上制作此图像 然后以某种方式将图像传递到 JSP 页面 以便它可以显示它 我试图避免保存图像 而是以某种方式将图像流式传输到 JSP 自从我开始寻
  • 内部类的构造函数引用在运行时失败并出现VerifyError

    我正在使用 lambda 为内部类构造函数创建供应商ctx gt new SpectatorSwitcher ctx IntelliJ建议我将其更改为SpectatorSwitcher new反而 SpectatorSwitcher 是我正
  • 尝试将 Web 服务部署到 TomEE 时出现“找不到...的 appInfo”

    我有一个非常简单的项目 用于培训目的 它是一个 RESTful Web 服务 我使用 js css 和 html 创建了一个客户端 我正在尝试将该服务部署到 TomEE 这是我尝试部署时遇到的错误 我在这里做错了什么 刚刚遇到这个问题 我曾
  • logcat 中 mSecurityInputMethodService 为 null

    我写了一点android应显示智能手机当前位置 最后已知位置 的应用程序 尽管我复制了示例代码 并尝试了其他几种解决方案 但似乎每次都有相同的错误 我的应用程序由一个按钮组成 按下按钮应该log经度和纬度 但仅对数 mSecurityInp
  • Eclipse 选项卡宽度不变

    我浏览了一些与此相关的帖子 但它们似乎并不能帮助我解决我的问题 我有一个项目 其中 java 文件以 2 个空格的宽度缩进 我想将所有内容更改为 4 空格宽度 我尝试了 正确的缩进 选项 但当我将几行修改为 4 空格缩进时 它只是将所有内容
  • 最新的 Hibernate 和 Derby:无法建立 JDBC 连接

    我正在尝试创建一个使用 Hibernate 连接到 Derby 数据库的准系统项目 我正在使用 Hibernate 和 Derby 的最新版本 但我得到的是通用的Unable to make JDBC Connection error 这是
  • Android:无法使用 DbHelper 和 Contract 类将数据插入 SQLite

    public class Main2Activity extends AppCompatActivity private EditText editText1 editText2 editText3 editText4 private Bu
  • 如何使用mockito模拟构建器

    我有一个建造者 class Builder private String name private String address public Builder setName String name this name name retur
  • 使用反射覆盖最终静态字段是否有限制?

    在我的一些单元测试中 我在最终静态字段上的反射中遇到了奇怪的行为 下面是说明我的问题的示例 我有一个基本的 Singleton 类 其中包含一个 Integer public class BasicHolder private static
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • Spring Boot 无法更新 azure cosmos db(MongoDb) 上的分片集合

    我的数据库中存在一个集合 documentDev 其分片键为 dNumber 样本文件 id 12831221wadaee23 dNumber 115 processed false 如果我尝试使用以下命令通过任何查询工具更新此文档 db

随机推荐

  • java后台实现模拟登陆

    一 原理 客户端访问服务器 服务器通过Session对象记录会话 服务器可以指定一个唯一的session ID作为cookie来代表每个客户端 用来识别这个客户端接下来的请求 我们通过Chrome浏览器进行网页访问时 服务器会在我们第一次请
  • NFS服务器设置及mount命令挂载

    一 NFS服务器的设定 NFS服务器的设定可以通过 etc exports这个文件进行 设定格式如下 分享目录 主机名称或者IP 参数1 参数2 arm2410s 10 22 22 rw sync no root squash 可以设定的参
  • Charles使用教程

    Charles Charles 是一款收费的抓包修改工具 易上手 数据请求容易控制 修改简单 抓取数据的开始暂停方便等优势 下面详细介绍下这款强大好用的抓包工具 抓包 packet capture 就是将网络传输发送与接收的数据包进行截获
  • 在Win10中安装虚拟机:VMware Workstation Pro16+Ubuntu20.04

    引言 本篇首先在Win10中安装虚拟机工具软件VMware Workstation Pro 然后按照鸿蒙设备开发环境的要求 用VMware Workstation Pro创建一个虚拟机 最后在虚拟机上安装Ubuntu20 04系统 鸿蒙设备
  • python 【raise函数】

    一 raise函数的作用 抛出自定义的异常 stackoverflow社区里面常说的 Manually raising throwing an exception in Python 这个manually解释的就很到位 是人工的 自己定义的
  • 【JavaScript高级】class类、ES6实现继承、ES6对象增强

    文章目录 class类 构造函数 访问器方法 getter和setter 静态方法 ES6实现继承 extends super 继承内置类 类的混入 了解 ES6对象的增强 字面量的增强 解构 解构相关应用 多态 参考 class类 ES6
  • java毕业论文云笔记_java毕业设计_springboot框架的云笔记记事本

    这是一个基于java的毕业设计项目 毕设课题为springboot框架的云笔记记事本 是一个采用b s结构的javaweb项目 开发工具eclipsei eclipse 项目框架jsp springboot mybatis 云笔记记事本采用
  • 命令式编程和声明式编程

    一 命令式编程 命令 机器 如何去做事情 how 这样不管你想要的是什么 what 它都会按照你的命令实现 注重过程 用详细的命令机器怎么去处理一件事情以达到你想要的结果 例如你想通过点击改变页面中某一个元素 首先要获取按钮 再给按钮添加点
  • 重新配对_Apple Watch配对失败的解决办法

    昨晚上我把那支用了将近三年的3代苹果表重置之后 打算重新配对 结果第一次碰上了配对失败的问题 弹窗提示我可能连接了不受信任的网络 前后用无线网和4G网络试了很多次 都是一样的结果 百度解决方法也完全没头绪 今天下午终于在苹果客服的帮助下解决
  • 这个落泪的男人叫王坚

    这个落泪的男人叫王坚 前段时间新闻报道 说一个名叫Watson的人工智能 花十几分钟读完2000万页医疗文献之后 解决了医生都束手无策的病情 听着感觉这人工智能跟打败世界棋手李世石的AlphaGO 阿尔法狗 相比弱爆了 但要知道那可是200
  • Vulkan入门(一)-环境配置.md

    文章目录 参考资料 简述 一 准备环境 1 1 开发环境 1 2 下载 SDK 1 3 安装SDK 1 4 安装驱动 1 5 运行示例程序 二 GLFW 安装 三 GLM 安装 四 手动编译示例代码 4 1 在编译示例代码的时候老是报错 找
  • MySQL 下载安装教程

    MySQL Community 8 0 安装教程 说明 步骤 说明 本教程只是 Windows 下 MySQL 的一种集成 IDE 的安装教程 安装此 IDE 还免去了手动在 Windows 上配置 MySQL 的麻烦 不过如果读者对 My
  • 国产 CAE 软件研发

    1 简介 国产 CAE 软件研发的特点 国家重大需求 自主知识产权 架构灵活 二次开发 通用化 定制化 2 CAE 软件研发 现状和意义 我国自主开发商业化程度高 通用性强的 CAE 软件 尤其是开发带良好图形用户界面的前后处理器的工作还十
  • RISC-V from scratch 5:机器模式

    RISC V from scratch 5 机器模式 接上一篇博客 我今天继续写 RISC V from scratch 系列博客 原本我打算将该英文系列全部翻译成中文 但原作者貌似没有把这一系列完成就咕咕了 因此本文的内容是我自己实践的内
  • 企业级远程桌面,需要考虑哪些核心因素?

    随着经济的发展 现代企业的管理模式也在发生改变 2020年初 新冠疫情爆发 所有的企业都无法按时返工 各个企业都开始寻求新的办公模式 埃森哲2021最新报告中有一个数据显示 87 的高管人员认为 远程劳动力为劳动人才市场打开了新天地 且扩大
  • 《信号与系统》解读 前言:经典教材的选择

    1 教材选择 信号与系统 系统的教材很多 分国内与国外教材 专题以 信号与系统 奥本海姆第二版为基础与主线 结合LTE 5G移动通信的工程实践需要 有选择性的对理论内容进行解读 2 主要内容 信号与系统 是美国麻省理工学院 MIT 的经典教
  • Stata计算可操纵性应计利润——基于琼斯模型

    说明 数据 变量名称来源于国泰安数据库 具体名称可见国泰安数据库资产负债表 利润表 本代码仅供参考 代码实现 基本Jones模型 Jones 1991 提出了经典的Jones模型 从营业收入变动和固定资产水平衡量企业应计利润的变动 clon
  • Java中文与Base64互转(解决中文乱码的问题)

    最近线上出现一个问题 前后端交互时 某些情况下 会有中文乱码的问题 解决思路 1 在后端先将中文转为 Base64 后再传递到前端 此中文在前端不做显示处理 2 前端将参数再传递回后端时 后端解析 Base64 得到中文字符串 packag
  • 9个免费的矢量图网站

    寻找一些特别的 为众所不知的矢量图网站不是一件容易的事情 又要高质量 又要免费使用 尽管鱼和熊掌不能兼得 但是谁叫我们碰到了互联网时代呢 谁叫我们知道一句台词 一切皆有可能呢 这些免费的矢量图网站是我在互联网上搜索到的 经过权衡和对比 选择
  • 设计模式——装饰模式

    装饰模式 1 装饰模式动机及定义 1 1模式动机 买了新房 毛胚房 需要装修 对新房进行装修并没有改变房子用于居住的本质 但它让房子变的更漂亮 更加满足居家的需求 在软件设计中 我们也可以用类似的技术对原有对象 新房 的功能进行扩展 装修