设计模式 -- 装饰者模式(Decorator Pattern)

1 问题引出

1.1 咖啡馆订单项目

  1. 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)

  2. 调料:Milk、Soy(豆浆)、Chocolate

  3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便

  4. 使用 OO 的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。

1 方案一

  1. Drink 是一个抽象类,表示饮料

  2. des 就是对咖啡的描述, 比如咖啡的名字

  3. cost() 方法就是计算费用,Drink 类中做成一个抽象方法.

  4. Decaf 就是单品咖啡, 继承 Drink, 并实现 cost

  5. Espresso && Milk 就是单品咖啡+调料, 这个组合很多

  6. 问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

2 方案二

        前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)

说明: milk,soy,chocolate 可以设计为 Boolean,表示是否要添加相应的调料.

2 定义

  1. 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)

  2. 这里提到的动态的将新功能附加到对象和 ocp 原则

3 原理

        装饰者模式就像打包一个快递

        主体:比如:陶瓷、衣服 (Component) // 被装饰者

        包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)

  1. Component 主体:比如类似前面的 Drink

  2. ConcreteComponent:具体的主体, 比如前面的各个单品咖啡

  3. Decorator: 装饰者,比如各调料

        如图的 Component 与 ConcreteComponent 之间,如果 ConcreteComponent 类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。

4 问题解决

4.1 图解

说明

  • Drink类就是前面说的抽象类,Component
  • ShortBlack就单品咖啡
  • Decorator是一个装饰类,含有一个被装饰的对象(Drink obj)
  • Decorator 的cost方法 进行一个费用的叠加计算,递归的计算价格

说明

  1. Milk包含了LongBlack
  2. 一份Chocolate包含了(Milk+LongBlack)
  3. 一份Chocolate包含了(Chocolate+Milk+LongBlack)
  4. 这样不管是什么形式的单品咖啡+调料组合,通过递归方式可以方便的组合和维护。

4.2 代码实现

Drink

public abstract class Drink {
​public String des; //  描述private float price = 0.0f;public String getDes() {return des;}public void setDes(String des) {this.des = des;}public float getPrice() {return price;
​}public void setPrice(float price) {this.price = price;}//  计算费用的抽象方法//  子类来实现public abstract float cost();
​
}

Chocolate

// 具体的 Decorator, 这里就是调味品
public class Chocolate extends Decorator {public Chocolate(Drink obj) { super(obj);setDes(" 巧克力 ");setPrice(3.0f); //  调味品 的价格}
}

Coffee

public class Coffee extends Drink {
​@Overridepublic float cost() {return super.getPrice();}
}

DeCaf

public class DeCaf extends Coffee {public DeCaf() {setDes(" 无因咖啡 "); setPrice(1.0f);}
}

Decorator

public class Decorator extends Drink {private Drink obj;
​public Decorator(Drink obj) { //  组合this.obj = obj;}
​@Overridepublic float cost() {//  getPrice 自己价格return super.getPrice() + obj.cost();}
​@Override public String getDes() {//  obj.getDes() 输出被装饰者的信息return des + " " + getPrice() + " && " + obj.getDes();}
​
}

Espresso

public class Espresso extends Coffee {public Espresso() {setDes(" 意大利咖啡 ");setPrice(6.0f);}
}

LongBlack

public class LongBlack extends Coffee {public LongBlack() {setDes(" longblack "); setPrice(5.0f);}
}

Milk

public class Milk extends Decorator {public Milk(Drink obj) { super(obj);setDes(" 牛奶 ");setPrice(2.0f);}
}

ShortBlack

public class ShortBlack extends Coffee{public ShortBlack() {setDes(" shortblack "); setPrice(4.0f);}
}

Soy

public class Soy extends Decorator{public Soy(Drink obj) { super(obj); setDes(" 豆浆 ");setPrice(1.5f);}
}

CoffeeBar

public class CoffeeBar {
​public static void main(String[] args) {//  TODO Auto-generated method stub//  装饰者模式下的订单:2 份巧克力+一份牛奶的 LongBlack
​//  1. 点一份 LongBlackDrink order = new LongBlack();System.out.println("费用 1=" + order.cost());System.out.println("描述=" + order.getDes());
​//  2. order 加入一份牛奶order = new Milk(order);
​System.out.println("order 加入一份牛奶 费用 =" + order.cost());System.out.println("order 加入一份牛奶 描述 = " + order.getDes());
​//  3. order 加入一份巧克力
​order = new Chocolate(order);
​System.out.println("order 加入一份牛奶 加入一份巧克力    费用 =" + order.cost());
​System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());
​//  3. order 加入一份巧克力
​order = new Chocolate(order);
​System.out.println("order 加入一份牛奶 加入 2 份巧克力  费用 =" + order.cost());System.out.println("order 加入一份牛奶 加入 2 份巧克力 描述 = " + order.getDes());
​System.out.println("===========================");
​Drink order2 = new DeCaf();
​System.out.println("order2 无因咖啡 费用 =" + order2.cost());System.out.println("order2 无因咖啡 描述 = " + order2.getDes());
​order2 = new Milk(order2);
​System.out.println("order2 无因咖啡 加入一份牛奶  费用 =" + order2.cost());System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes());
​}
​
}

5 优缺点

5.1 优点

  1. 扩展灵活:使用装饰者模式比继承更加灵活,可以在运行时为对象添加新的行为,无需修改原有代码,符合开闭原则。
  2. 排列组合:通过使用不同的具体装饰类及其排列组合,设计师可以创造出多种不同行为的组合,从而增加系统的灵活性和可扩展性。
  3. 低耦合:装饰者与被装饰对象之间是松耦合关系,易于替换和扩展,有利于降低系统各部分之间的依赖关系。
  4. 避免类爆炸:相较于继承,装饰者模式避免了大量子类的产生,简化了系统的设计。
  5. 易于撤销:可以随时为对象添加或撤销功能,这在实现一些需要动态调整功能的场景中非常有用。

5.2 缺点

  1. 多对象生成:使用装饰者模式会产生更多的对象,这些对象如果管理不当,可能会使查错变得困难,尤其是当这些对象看上去都很相似时。
  2. 复杂性增加:虽然装饰者模式可以减少类的数目,但会增加对象的数量,从而在一定程度上增加了系统的复杂性。
  3. 过度使用问题:如果过度使用装饰者模式,会导致设计中出现许多小对象,从而使程序变得复杂。

6 应用场景

  1. 在java.io 包中,定义了大量的类,这些类可以创建流对象以进行读写操作。其中许多类的设计都使用了装饰者模式。例如,将BufferedReader 装饰到 FileReader 上,这样就可以从文件中读取文本了。
  2. Spring Session 中的装饰者模式 Spring Session 提供了一种透明的方式来支持多种存储方式,如Redis、Memcached等。其核心就是使用装饰者模式来包装Session接口的实现。
  3. MyBatis缓存机制中的装饰者模式 为了方便用户自定义或扩展缓存机制,MyBatis在其内部通过装饰者模式来实现缓存机制。具体地,MyBatis允许用户提供一个自定义的Cache接口实现,然后通过装饰者包装这个实现,以便在查询结果之前清空缓存。

7 总结

        综上所述,装饰者模式是一种结构型设计模式,它允许动态地向一个现有的对象添加新的功能,同时又不改变其结构。这种模式提供了一种灵活且低耦合的方法来扩展对象的功能,但它也可能带来一定的复杂性,因此在使用时应权衡利弊,合理设计。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无人机之云台的作用

无人机云台在无人机技术中扮演着至关重要的角色,其作用主要体现在以下几个方面: 一、 确保拍摄稳定性 防抖动:无人机在飞行过程中,尤其是在复杂环境下,如遇到风力干扰或进行高速飞行时,机身容易产生震动和…

Beyond Compare忽略特定格式文本,忽略匹配正则表达式

一 概述 文本对比时忽略某些文本。比如有些生成的文件需要做差异对比,除了内容有差异外,自动生成的ID也不同,想忽略这些ID。特别是文件内容比较多的时候。 如上图,其中UUID“*”的部分我想忽略。 二 方法 方法1 通过Beyond Co…

MySQL 中间件 MySQL-Router

目录 1 MySQL-Router 的介绍 2 MySQL-Router 负载均衡 2.1 设计目的: 2.2 HAProxy 与 Nginx 和 MySQL-Router 之间的区别 2.3 MySQL-Router 的优势 3 MySQL-Router 的获取 3 MySQL-Router 的使用 3.1 实验环境 3.2 MySQL-Router 部署 3.3 MySQL-Router 配置 3.4 测…

HarmonyOS--合理使用动画

一、概述 动画是应用开发中必不可少的部分,它可以使应用程序更加生动和易于互动,一方面可以提升用户体验、增强视觉吸引力,另一方面可以引导用户操作、提高信息传达效率。应用程序中,页面层级间的转场、点击交互、手势操控都可以添…

ODOO17文档打印(输出)方案 -- ODOO17 document printing (output) scheme

根据使用场景不同,ODOO17支持以下几种文档打印(输出)方案: According to different usage scenarios, ODOO17 supports the following document printing (output) schemes: 1、QWEB ODOO原生打印功能(生成PDF文档) odoo使用的主…

【AI】:探索在图像领域的无限可能

欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 文章目录 图像识别与分类的飞跃图像生成与创造的艺术图像增强与修复的神奇图像搜索与理解的智能图像分析与挖掘的洞察图形生成技术1. 生成对抗网络(GANs)2. 卷积神经网络(CN…

多语言跨领域迁移学习的新框架:MAD-X

人工智能咨询培训老师叶梓 转载标明出处 多语言模型如mBERT和XLM-R通过零样本或少样本跨语言迁移极大地推动了低资源语言的NLP应用。但这些模型由于容量限制,对低资源语言和未见语言的迁移性能并不理想。为了解决这一问题,来自德国达姆施塔特工业大学、…

Stable Diffusion详解

文章目录 前言一、LDM原理二、模型结构三、模型训练与推理总结 前言 Stable Diffusion在图像生成方面取得了很大的成功,其核心原理是LDM(Latent Diffusion Models),在论文《High-Resolution Image Synthesis with Latent Diffusio…

【数据结构】优先级队列 — 堆

文章目录 前言1. 优先级队列1.1 概念1.2 特性 2. 堆2.1 概念2.2 存储方式 3. 堆的模拟实现3.1 堆的创建3.2 堆的插入3.3 堆的删除 4. PriorityQueue4.1 注意事项4.2 构造器介绍4.3 常用方法介绍 5. 经典题型6. 结语 前言 我们之前学习过队列,它是遵循先进先出原则的…

halcon 深度学习软件工具安装以及用法

安装halcon 20版本以上得 以为这个版本以上得有异常检测,分割,分类,目标检测,都有 一、下载软件 可以再官网下载,但是官网要注册账号 下载区域: MVTec Software 不用官方的账号 就下载安装包 链接:http…

day13JS-MoseEvent事件

1. MouseEvent的类别 mousedown :按下键mouseup :释放键click :左键单击dblclick :左键双击contextmenu :右键菜单mousemove :鼠标移动mouseover : 鼠标经过 。 可以做事件委托,子元素可以冒泡…

使用Blender进行3D建模—基础操作笔记

Blender 3D 建模🚀 在博0阶段,目前已经完成立创EDA的PCB绘制的基础学习,树莓派的系统安装远程控制能学习,加上我本硕阶段学习的单片机和深度学习人工智能算法的知识,这里打算补上一块比较重要的能力拼图,就…

Netty 学习笔记

Java 网络编程 早期的 Java API 只支持由本地系统套接字库提供的所谓的阻塞函数,下面的代码展示了一个使用传统 Java API 的服务器代码的普通示例 // 创建一个 ServerSocket 用以监听指定端口上的连接请求 ServerSocket serverSocket new ServerSocket(5000); //…

c++关于字符串的练习

提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 #include <iostream> #include<string> using namespace std;int main() {string s1;int letter0,digit0,space0,other0;cout<<"请输入一个字符串:"…

海康二次开发学习笔记5-二次开发小技巧

二次开发小技巧 1. VM安装目录 Samples内包含C#,QT,VC应用程序 Documetnations内包含C#和C语言的帮助文档 2. 错误码 private void button4_Click(object sender, EventArgs e){try{VmSolution.Load(textBox1.Text);listBox1.Items.Add("方案加载成功.");listBox1.…

质量技术AI提效专题分享-得物技术沙龙

活动介绍 本次“质量技术&AI提效专题分享”沙龙聚焦于质量技术和AI效率领域&#xff0c;将为您带来四个令人期待的演讲话题&#xff1a; 1、《智能化提效实践》 2、《仿真自动化在饿了么金融实践分享》 3、《得物精准测试提效应用》 4、《广告算法灰度拦截实践》 相信这些…

开源的工作流系统突出优点总结

当前&#xff0c;想要实现高效率的办公&#xff0c;可以一起来了解低代码技术平台、开源的工作流系统的相关特点和功能优势。作为较受职场喜爱的平台产品&#xff0c;低代码技术平台拥有可视化才做界面、灵活、好维护操作等多个优势特点&#xff0c;在推动企业流程化办公的过程…

读软件开发安全之道:概念、设计与实施12不受信任的输入

1. 不受信任的输入 1.1. 不受信任的输入可能是编写安全代码的开发人员最关心的问题 1.1.1. 最好将其理解为输入系统中的所有不受信任的输入 1.1.2. 来自受信任的代码的输入可以提供格式正确的数据 1.2. 不受信任的输入是指那些不受你控制&#xff0c;并且可能被篡改的数据&…

RASA使用长文记录以及一些bug整理

RASA 学习笔记整理 一 安装 在虚拟环境中安装&#xff0c;进入python3版本的环境 conda activate python3 ai04机器旧版本&#xff1a;rasa-nlu和rasa-core是分开安装的 最新版本&#xff1a;rasa 将二者做了合并 直接安装 pip3 install rasa 在安装到如下步骤时候会报…

github上传代码

一般要上传github代码有两种模式&#xff0c;一种是直接在repo中上传&#xff0c;一种是通过git来上传&#xff08;win和linux都可以&#xff09;&#xff0c;来学习一下。 我们去创建好一个repo后&#xff1a; 首先是直接上传&#xff08;不推荐&#xff09; 通过upload file…