设计模式十:装饰器(Decorator)

现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等,都是装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时为对象动态地添加额外的行为或功能,而无需修改原始类的代码。该模式通过创建一个包装器,即装饰器,来包裹原始对象,并以透明的方式扩展其功能。装饰器和原始对象都实现了相同的接口,这使得它们可以互相替代使用。

装饰器模式适用于以下场景:

装饰器模式适用于需要在运行时动态地扩展对象功能,以及对核心对象进行修改但又不希望直接修改其代码的情况。它提供了一种灵活且无侵入性的方式来添加新功能,并允许根据需要组合多个装饰器以实现不同的行为变化

  1. 当你需要在不修改现有对象代码的情况下,动态地给一个对象添加新的行为或功能时,可以使用装饰器模式。它允许你通过包装对象来扩展其功能。
  2. 当你希望能够在运行时选择要添加的特定行为时,装饰器模式非常有用。你可以根据需要动态地组合多个装饰器,以实现不同的行为组合效果。
  3. 当你有一个核心对象,并且希望能够在不影响其他对象的情况下对其进行修改时,装饰器模式是一种很好的选择。由于装饰器模式使用了继承关系,因此你可以针对不同的需求创建多个具体装饰器类,并通过组合它们以满足所需的行为变化。这样就避免了直接修改核心对象的代码。
  4. 当你想要以层次结构的方式动态地组合对象时,可以使用装饰器模式。你可以通过创建多个具体装饰器类并将它们嵌套在一起,形成一个层次结构,从而实现更复杂的行为组合。
  5. 当你想要在不同的时间点为一个对象添加或删除特定行为时,装饰器模式是一个很好的选择。你可以根据需要随时添加或删除特定的装饰器,而不需要修改对象的代码。

装饰器模式中的核心角色包括:

  1. 抽象构件(Component):定义了原始对象和装饰器的公共接口。
  2. 具体构件(Concrete Component):实现了抽象构件的接口,表示原始对象。
  3. 装饰器(Decorator):持有一个抽象构件的引用,并实现了抽象构件接口,用于添加额外的行为或功能。
  4. 具体装饰器(Concrete Decorator):扩展了装饰器的功能,可以在调用原始对象的方法之前或之后执行额外的操作。

装饰器模式代码实践

用装饰模式来完成Coffee的制作具体实现
要求:Coffee有两种款式,浓Coffee和淡Coffee
利用装饰模式加料:牛奶,巧克力
涉及的所有类:

抽象构件:Coffee

/*** coffee制作接口:抽象构件接口,表示咖啡 */
ublic interface Coffee {String getDescription();//获取对应的描述 int getPrice();//获取价格
}

具体构件:LightCoffee、StrongCoffee

public class LightCoffee implements Coffee{@Override    public String getDescription() {return "you will get a cap of LightCoffee"; }@Override    public int getPrice() {return 18;    }
}
/*** 浓咖啡*/
public class StrongCoffee implements Coffee {@Override    public String getDescription() {return "you will get a cap of StrongCoffee";}@Override    public int getPrice() {return 20;//浓咖啡价格为十元}
}

抽象装饰器:CoffeeDecorator

/*** 实现一个抽象的Coffee装饰器*/
public abstract class CoffeeDecorator implements Coffee {protected Coffee coffee;public CoffeeDecorator(Coffee coffee) {this.coffee = coffee;}public String getDescription() {return coffee.getDescription();}public int getPrice() {return coffee.getPrice();}}

具体装饰器:Chocolate、Milk、Mocha

public class Chocolate extends CoffeeDecorator {public Chocolate(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return coffee.getDescription() + " with Chocolate";}@Overridepublic int getPrice() {return coffee.getPrice() + 12;}}public class Milk extends CoffeeDecorator {public  Milk(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return coffee.getDescription()+" with Milk";}@Overridepublic int getPrice() {return coffee.getPrice() + 8;}}public class Mocha extends CoffeeDecorator{public Mocha(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return coffee.getDescription()+" with Mocha";}@Overridepublic int getPrice() {return coffee.getPrice() +16;}}

运行结果:

public static void main(String[] args) {//创建一杯浓coffeeCoffee strongCoffee = new StrongCoffee();System.out.println(strongCoffee.getDescription() + "~it cost :" + strongCoffee.getPrice());//创建一杯带有牛奶的浓coffeeCoffee strongCoffeeWithMilk = new Milk(strongCoffee);System.out.println(strongCoffeeWithMilk.getDescription() + "~it cost :" + strongCoffeeWithMilk.getPrice());//创建一杯牛奶和巧克力的淡CoffeeCoffee lightCoffee = new LightCoffee();Coffee lightCoffeeWithMilk = new Milk(lightCoffee);Coffee lightCoffeeWithMilkAndChocolate = new Chocolate(lightCoffeeWithMilk);System.out.println(lightCoffeeWithMilkAndChocolate.getDescription() + "~it cost :" + lightCoffeeWithMilkAndChocolate.getPrice());}

you will get a cap of StrongCoffee~it cost :20
you will get a cap of StrongCoffee with Milk~it cost :28
you will get a cap of LightCoffee with Milk with Chocolate~it cost :38

以上示例演示了如何使用装饰器模式来制作咖啡。通过创建具体的咖啡类和装饰器类,我们可以在运行时动态地添加额外的配料或功能,而不需要修改原始咖啡类的代码。

装饰器模式的优缺点

装饰器模式的优点包括:

  1. 动态扩展功能:装饰器模式允许在运行时动态地为对象添加新的功能,而无需修改现有代码。这使得系统更具灵活性和可扩展性。
  2. 组合多个装饰器:通过使用多个装饰器,你可以在对象上组合不同的功能,以实现各种组合效果。这样可以避免类的继承关系变得过于复杂,提高了代码的可读性和可维护性。
  3. 分离关注点:装饰器模式将功能的添加与核心对象的实现分离开来,使得各个功能模块可以独立开发和测试。这使得代码更加清晰、易于理解和维护。
  4. 遵循开闭原则:装饰器模式符合开闭原则,即对扩展开放,对修改关闭。通过使用装饰器模式,你可以在不修改现有代码的情况下,通过添加新的装饰器来扩展功能。

装饰器模式的缺点包括:

  1. 可能产生过多的小对象:装饰器模式涉及创建多个对象,并将它们嵌套在一起,可能会导致系统中存在大量的小对象。这可能会增加内存使用和运行时开销。
  2. 可能增加复杂度:当装饰器的层次结构变得复杂时,理解和维护代码可能会变得困难。过多的装饰器可能会使代码变得混乱并且难以debug。
  3. 需要注意继承问题:装饰器模式是通过继承来实现的,因此需要特别注意父类与子类之间的关系。如果不正确地设计和使用装饰器模式,可能会导致继承关系变得复杂和脆弱。

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

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

相关文章

Android前沿技术?Jetpack如何?

Jetpack Compose是Android开发领域的一项前沿技术,它提供了一种全新的方式来构建用户界面。近年来,Jetpack Compose在各大招聘等网站上的招聘岗位逐渐增多,薪资待遇也相应提高。本文将从招聘岗位的薪资与技术要求入手,分析Jetpack…

多线程的创建,复习匿名内部类,Thread的一些方法,以及lambda的变量捕捉,join用法

一、💛 Java的Thread类表示线程 1.创建类,继承Thread重写run方法 2.创建类,实现Runnable重写run方法 3.可以继承Thread重写run基于匿名内部类 4.实现Runnable重写run基于匿名内部类 5.lamdba表达式表示run方法的内容(推荐&#x…

适配器模式

**适配器模式(Adapter Pattern)**是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口,从而使原本由于接口不兼容而无法一起工作的类能够一起工作。 适配器模式主要有三个角色: 目标接口&…

16-3_Qt 5.9 C++开发指南_使用QStyle 设置界面外观_实现不同系统下的界面效果的匹配

文章目录 1. QStyle的作用(实现不同系统下的界面效果的匹配)2. Qt内置样式的使用3. 源码3.1 可视化UI设计3.2 mainwindow.cpp 1. QStyle的作用(实现不同系统下的界面效果的匹配) Qt 是一个跨平台的类库,相同的界面组件…

算法与数据结构-跳表

文章目录 什么是跳表跳表的时间复杂度跳表的空间复杂度如何高效的插入和删除跳表索引动态更新代码示例 什么是跳表 对于一个单链表来讲,即便链表中存储的数据是有序的,如果我们要想在其中查找某个数据,也只能从头到尾遍历链表。这样查找效率…

虚继承中对象占用的内存空间

1、虚继承中对象占用的内存空间1 #include <iostream> using namespace std;class AA {void show() {}int max(int a, int b) { return a > b ? a : b; } }; //函数并不占用内存空间class A {}; //占位符class B {int c; }; //含有一个int型数据成员class C :vi…

1. 软件生命周期C/S、B/S 架构

目录 1. 软件生命周期 2. 面向对象 2.1 面向对象分析 2.2 面向对象设计 2.3 面向对象编程 3. C/S、B/S 架构 3.1 CS 架构 3.2 BS 架构 1. 软件生命周期 软件生命周期中划分为可行性研究、需求分析、概要设计、详细设计、实现、组装(集成)测试、 确认测试、使用、维护…

如何在shell脚本将node_modules里的文件复制一份到public文件里

项目背景&#xff1a;由于公司网络不连接公网&#xff0c;所以在绘制地图大屏项目时&#xff0c;需要我们将边界线数据包也部署起来&#xff0c;来获取边界线数据 解决方案&#xff1a; 1.让后端写个接口或者找个地方将数据包放到服务器即可 2.将数据包放到vue项目的public文…

LiveGBS流媒体平台GB/T28181常见问题-无法注册不上海康NVR摄像机自带物联网卡摄像头注册GB/T28181国标平台看不到设备的时候如何抓包及排查

LiveGBS无法注册不上海康NVR摄像机自带物联网卡摄像头注册GB/T28181国标平台看不到设备的时候如何抓包及排查 1、设备注册后查看不到1.1、是否是自带物联网卡的摄像头1.2、关闭萤石云1.3、防火墙排查1.4、端口排查1.5、IP地址排查1.6、设备TCP/IP配置排查1.7、设备多网卡排查1.…

【C++从0到王者】第十七站:手把手教你写一个stack和queue及deque的底层原理

文章目录 一、stack1.利用适配器2.栈的实现 二、queue三、deque1.deque介绍2.deque的接口3.deque的基本使用4.deque的效率5.deque的原理 一、stack 1.利用适配器 我们不可能写了一份数组栈以后&#xff0c;还要在手写一个链式栈&#xff0c;这样显得太冗余了。于是我们可以利…

详解Linux中的socket函数

2023年8月3日&#xff0c;周四下午 目录 函数原型参数domain参数type参数protocol举例说明参数type和参数protocol之间的关系 函数原型 #include <sys/socket.h>int socket(int domain, int type, int protocol);参数domain domain是“域”的意思&#xff0c;其值为AF…

Redis未授权访问漏洞

Redis未授权访问漏洞 一、未授权访问漏洞概述、二、Redis未授权访问特征三、Redis常用命令四、Redis历史漏洞4.1、Redis未授权访问4.2、Redis主从复制RCE 五、Reids未授权访问利用5.1、写webshell5.2、写定时任务反弹shell 一、未授权访问漏洞概述、 未授权访问漏洞可以理解为需…

【C++】Lambda表达式的使用

学习目标&#xff1a; 例如&#xff1a; 了解Lambda的优点 掌握Lambda表达式的使用 了解Lambda表达式的底层原理 学习内容&#xff1a; Lambda表达式的语法 文章目录 学习目标&#xff1a;学习内容&#xff1a;Lambda表达式排序案例Lambda表达式语法捕捉列表Lambda表达式模拟…

Javascript 数据结构[入门]

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于海外某世界知名高校就读计算机相关专业。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。…

3年经验,面试测试岗只会功能测试开口要求18K,令我陷入沉思。

由于朋友临时有事&#xff0c; 所以今天我代替朋友进行一次面试&#xff0c;公司需要招聘一位自动化测试工程师&#xff0c;我以很认真负责的态度完成这个过程&#xff0c; 大概近30分钟。 主要是技术面试&#xff0c; 在近30分钟内&#xff0c; 我与被面试者是以交流学习的方式…

java linq多字段排序时间比较

public static void main(String[] args) {//100万条数据List<CrmInvestSaleUserCount> waitAssignUserList new ArrayList<>();for (int i 0; i < 1000000; i) {waitAssignUserList.add(new CrmInvestSaleUserCount().setSales_username("test" i…

架构训练营学习笔记:6-2 微服务基础选型

基础选型 微服务基础设施架构 优先级 其中&#xff0c;核心 就是服务注册、服务发现、服务路由。 模式1-嵌入SDK 模式2-反向代理式 模式3-网络代理式&#xff08;Service Mesh&#xff09; 模式对比 常见微服务框架选择 嵌入SDK-dubbo Spring Cloud 反向代理式 APISIX …

小研究 - 基于 SpringBoot 微服务架构下前后端分离的 MVVM 模型(一)

本文主要以SpringBoot微服务架构为基础&#xff0c;提出了前后端分离的MVVM模型&#xff0c;并对其进行了详细的分析以及研究&#xff0c;以此为相关领域的工作人员提供一定的技术性参考。 目录 1 研究背景 2 SpringBoot微服务优势 3 微服务 3.1 技术发展 3.2 技术优势 在…

流数据湖平台Apache Paimon(五)集成 Spark 引擎

文章目录 第4章 集成 Spark 引擎4.1 环境准备4.2 Catalog4.2.1 文件系统4.2.2 Hive 4.3 DDL4.3.1 建表4.3.2 修改表 第4章 集成 Spark 引擎 4.1 环境准备 Paimon 目前支持 Spark 3.4、3.3、3.2 和 3.1。课程使用的Spark版本是3.3.1。 1&#xff09;上传并解压Spark安装包 t…

MyBatis枚举映射类讨论

前言 本篇需要对于MyBatis有一定的认识&#xff0c;而且只是针对于TypeHandler接口来讨论&#xff0c;暂不讨论其他方面的问题 TypeHandler概叙 TypeHandler是MyBatis设计的一个用于参数的接口&#xff0c;你们会不会很好奇MyBatis是如何把整形&#xff0c;时间&#xff0c;字符…