设计模式第5式:装饰器模式

news2024/9/27 19:14:23

前言

当我们初学编程时,扩展程序功能一般习惯使用继承,使用继承有一些缺点,那就是容易造成类爆炸,并且容易继承一些不需要的特性。当我们学习完装饰器模式后,会发现善用组合会有比继承更好的效果。

正文

1、咖啡馆案例

我们还是来看《Head First设计模式》给出的一个实际场景,一个咖啡馆要做一个订单系统。这个咖啡馆有4款基础咖啡(HouseBlend、DarkRoast、Decaf、Espresso),在基础咖啡之上我们可以加3种调料(Milk、Soy、Mocha),客人可以随意组合1款基础咖啡和多种调料,最后计算出总价。
在这里插入图片描述

最笨的办法就是穷举基础咖啡和调料的组合,光是一种基础咖啡加一种调料就要实现4*3=12个子类。这是典型的类爆炸。而且极不容易扩展,想象一下如果要再加一种基础咖啡或调料,又要新增多少个类。
在这里插入图片描述

再想一种办法,我们将是否加料放在父类Beverage中,将各种调料的布尔值放在父类中,于是父类中的cost方法要加上各种判断,最终算出调料总价。基础咖啡子类继承父类,cost方法将基础咖啡的价钱加上父类中的调料价钱,算出总价。这种方式的弊端是什么呢?调料价钱的改变或新减调料都会修改现有代码,而且有的基础咖啡并不需要继承所有调料,而且如果想要双倍调料怎么办?
在这里插入图片描述

利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。但是,如果使用组合的方式扩展对象的行为,就可以在运行时动态的扩展。通过动态的组合对象,可以写新的代码添加新的功能,无需修改现有的代码。既然没有修改现有代码,那么引入bug的机会将大大减少。

设计原则:类应该对扩展开发,对修改关闭

2、用装饰者模式改造案例

我们以基础饮料为主体,然后在运行时以调料来“装饰”饮料。比如顾客想要摩卡和奶泡深焙咖啡,那么要做以下步骤:
a、拿一个深焙咖啡DarkRoast对象;
b、以摩卡Mocha对象装饰它;
c、以奶泡Whip对象装饰它;
d、调用cost方法,并委托每一层装饰器和主题计算总价。

于是我们可以得到装饰器模式的定义:动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

  • 装饰者和被装饰者有相同的超类;
  • 可以用一个或多个装饰器包装一个对象;
  • 既然有相同的超类型,那么在任何需要被装饰对象的场合,都可以用装饰器来代替;
  • 装饰器可以在被装饰者的行为前后加上自己的行为,以达到特定的目的;
  • 可以在运行时动态地,不加限制的使用装饰器来装饰对象;
    在这里插入图片描述
    按照上面装饰器的类图,我们来重新画一下咖啡馆应用的类图:
    在这里插入图片描述

我们可以在装饰器的父类中持有Beverage对象,也可以在具体的装饰器对象中持有Beverage对象,本例中是在具体子类中:

Public class Mocha extends CondimentDecorator {
	Beverage beverage;

	public Mocha(Beverage beverage) {
		this.beverage = beverage;
	}

	public double cost() {
		return 0.2 + beverage.cost();
	}
}

按照类图写完代码后,让我们真正来点一杯加料的咖啡吧。

public static void main(String args[]) {
	Beverage beverage = new DarkRoast() // 先制造一个基础咖啡
	beverage = new Mocha(beverage); // 加一分Mocha
	beverage = new Mocha(beverage); // 再加一分Mocha
	beverage = new Whip(beverage); // 加一分Whip
	sout(beverage.cost()); // 计算总价
}

3、装饰者模式在Java I/O中的应用

java.io包中的类简直是多如牛毛,初学者根本无从下手,但是我们已经学习了装饰者模式,那就来抽丝剥茧,捋一捋这些类的关系。IO体系只提供了几个基础类提供了最基本的字节读写功能,比如FileInputStream、StringBufferInputStream、ByteArrayInputStream。其他都是这些基础类的装饰器,也就是在这些基础类上额外增加了一些功能。

在这里插入图片描述
那么,装饰器的抽象类是谁呢?那就是下图中的FilterInputStream,我们可以继承这个类实现自己的装饰器。我们看到FilterInputStream中持有了一个InputStream类型的引用。

public class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered.
     */
    protected volatile InputStream in;

	...
}

在这里插入图片描述

总结

装饰者模式适用于这样一种场景:有一些类提供基础的功能,还有一些类要增强这些基础类的功能。于是我们将增强功能类叫做装饰者,装饰者持有一个基础类型的引用,利用组合的形式对基础类进行功能增强。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/186884.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全志A40i+Logos FPGA开发板(4核ARM Cortex-A7)硬件说明书(上)

前 言 本文档主要介绍TLA40iF-EVM工业评估板硬件接口资源以及设计注意事项等内容。 核心板的ARM端和FPGA端的IO电平标准一般为3.3V,上拉电源一般不超过3.3V,当外接信号电平与IO电平不匹配时,中间需增加电平转换芯片或信号隔离芯片。按键或接口需考虑ESD设计,ESD器件选型时需…

深入Java自动化探针技术的原理和实践

转至作者 蒋志伟:深入Java自动化探针技术的原理和实践 前言建议阅读时间 30~40分钟读者需要对Java JVM 一定了解,文章会系统的介绍Java 探针核心原理和技术实现,总结目前一些主流的框架方案。同时,接下来我会分享一篇关于 OpenTel…

你是如何学会正则表达式的?

前言 前言 正则表达式作为对字符串操作的一种逻辑公式,它使用一些特定字符及其组合组成“规则字符串”来对字符串进行过滤的操作,如在注册验证的时候我们就经常会用到正则表达式,但正则表达式的变式太多,我们不用完全的去记住每一…

XSSed通关教程

XSSed通关教程 首先整体浏览网站 进入Level1 Basic XSS 首先整体浏览网站 对源码进行分析 漏洞产生于如下代码段&#xff1a; echo($_GET[‘q’]); 直接将用户输入插入了html页面&#xff0c;没有任何过滤。 构造普通payload&#xff1a; <script>alert(/xss/)<…

剑指 Offer II 004. 只出现一次的数字

题目链接 剑指 Offer II 004. 只出现一次的数字 mid 题目描述 给你一个整数数组 nums&#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 示例 1&#xff1a; 输入&#xff1a;nums [2,2,3,2] 输出&…

LeetCode042之接雨水(相关话题:动态规划,单调栈)

题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,…

vue复习+vuex

一、vue.js的基本指令1、Vue的作用&#xff1a;快速的构建前端页面&#xff08;封装了html、css、js&#xff09;,以工程化的方式进行前端的开发2、Vue的核心&#xff1a;&#xff08;1&#xff09;组件化&#xff1a;&#xff08;2&#xff09;数据双向绑定&#xff08;3&…

linux基本功系列之sudo命令实战一

文章目录一.sudo命令介绍二. 语法格式及常用选项三. sudo配置文件详解3.1 sudo的配置文件3.2 配置文件注释3.3 sudo授权规则四. sudo常用参数实战 参考案例4.1 查看当前用户有哪些被sudo服务授权的命令4.2 结束密码的有效期4.3 限制用户的权限总结前言&#x1f680;&#x1f68…

3分钟解读ISO27001信息安全管理体系

一、什么是ISO27001 ISO27001是信息安全管理体系认证&#xff0c;对应国标号GB/T22080-2016&#xff0c;企业建立ISO27001体系能有效保证企业在信息安全领域的可靠性&#xff0c;降低企业泄密风险&#xff0c;更好的保存核心数据和重要信息。 信息安全对每个企业都是非常重要的…

Spring Cloud 高频面试题25连环炮!

今天给大家分享SpringCloud高频面试题。 Spring Cloud核心知识总结 下面是一张Spring Cloud核心组件关系图&#xff1a; 从这张图中&#xff0c;其实我们是可以获取很多信息的&#xff0c;希望大家细细品尝。 话不多说&#xff0c;我们直接开始 Spring Cloud 连环炮。 连环…

【算法基础】链表与邻接表

在机试、面试中,使用链表通常不是结构体构建Node,申请新节点new操作,因为速度过慢,申请10w量级的空节点就已经超时了。因为在算法竞赛中,常常使用 一、数组模拟单链表【⭐邻接表(n个链表)⭐】 邻接表(n个链表)最主要的应用是存储图和存储树。 建表、插入、删除操作…

ThinkPad R490电脑开机之后无线重启怎么重装系统?

ThinkPad R490电脑开机之后无线重启怎么重装系统&#xff1f;有用户使用ThinkPad R490电脑正常开机的情况下&#xff0c;出现了系统自动重启的情况&#xff0c;无法正常的使用电脑了。遇到这个情况怎么去重装一个新的电脑系统&#xff0c;恢复正常使用呢&#xff1f;来看看以下…

17. datetime模块

python 标准库中的 datetime 模块提供了和日期和时间相关的类。 类功能datetime.date以年、月和日表示日历中的日期datetime.time以小时、分钟和秒表示一天中的时间datetime.datetime以年、月、日、小时、分钟和秒表示日期和时间datetime.timedelta表示一个时间段&#xff0c;…

什么蓝牙耳机便宜音质好?平价高音质蓝牙耳机推荐

随着蓝牙耳机的品类越来越多&#xff0c;人们在选择时有了更大的空间。作为蓝牙耳机选择的两大参考要素&#xff0c;性价比和音质的出现频率相对来说会比较高。那么&#xff0c;什么蓝牙耳机便宜音质好&#xff1f;下面&#xff0c;我来给大家推荐几款平价高音质的蓝牙耳机&…

初探机器学习-梯度下降法求解最优值

文章目录什么是模型如何训练模型1、拟定假设函数2、损失函数和代价函数3、关于导数和偏导数4、使用梯度下降法求解最优值5、回顾总结三、衡量一个模型的好坏模型验证1、简单交叉验证2、K 折交叉验证3、留一交叉验证过拟合什么是模型 只要是从事IT行业&#xff0c;想必都对机器…

基于Android的房屋租赁系统

需求信息&#xff1a; 房东客户端&#xff1a; 1&#xff1a;注册登录&#xff1a;使用分配的账号进行登录&#xff1b; 2&#xff1a;发布房源&#xff1a;房主可以发布自己的房源信息&#xff1b; 3&#xff1a;预约信息&#xff1a;查看租客的预约看房信息&#xff1b; 4&am…

sqllineage解析FineBI数据集导入Datahub生成血缘

需求 当前数仓架构流程图如下图所示&#xff0c;不支持端到端数据血缘&#xff0c;数据异常排查及影响分析比较被动&#xff0c;需要端到端数据血缘及元数据管理。 业务系统&#xff1a;各种制造业业务系统&#xff08;高速迭代、重构、新建中&#xff09; 数仓开发平台&…

jChartFX Plus JavaScript 7.6.7367 Crack

jChartFX Plus包括 jChartFX 的所有功能和其他画廊类型、高级商业智能功能和用于您的 Web 或移动应用程序的仪表板功能。新的 jChartFX Plus 为您提供额外的能力来开发完整的商业仪表板和商业智能应用程序 在最新的 jChartFX Plus 中&#xff0c;我们引入了象形图和象形图条控件…

仿牛客论坛项目Docker部署(ElasticSearch+Redis+Kafka+MySQL)

仿牛客论坛项目项目部署(docker)1.ElasticSearch安装本体安装ik插件2.Zookeeper3.Kafka测试是否启动成功4.MySQL启动mysql测试载入数据5.Redis6.DockerFile构建Java8项目部署成功参考项目部署(docker) 1.ElasticSearch 安装本体 mkdir -p /opt/docker/es/plugins #创建插件文…

jspssm小区车位物业管理系统

目 录 1 绪论 1 1.1 研究背景 1 1.2 小区物业管理系统的现状 1 1.3 系统实现的功能 1 1.4 小区物业管理系统的特点 2 1.5 本文的组织结构 2 1.6 系统分析 2 2 开发技术与环境配置 3 2.1 ssm框架 3 2.2 JSP技术 3 2.3 JavaScript 4 …