二十三种设计模式全面解析-装饰器模式-超越继承的灵活装扮


在软件开发中,我们经常面临需要为对象动态地添加额外的功能或属性的情况。继承是一种常见的解决方案,但它有时会导致类的爆炸性增长和复杂的继承层次结构。在这种情况下,装饰器模式(Decorator Pattern)是一种强大的设计模式,能够帮助我们实现灵活的组合和装饰对象,而无需依赖于继承关系。


本文将深入解析装饰器模式,包括装饰器模式的基本概念、适用场景、技术要点以及详细的案例代码。让我们一起探索装饰器模式的魅力,为软件设计带来全新的可能性。

1、什么是装饰器模式?

装饰器模式属于结构型设计模式,它允许我们在运行时动态地给对象添加新的行为或属性,而无需修改其原始类。装饰器模式通过将对象包装在一个装饰器类中,然后将装饰器类嵌套在其他装饰器类中,从而形成一个装饰器链。


一个对象可以使用多个类的行为, 包含多个指向其他对象的引用, 并将各种工作委派给引用对象; 继承中的对象则继承了父类的行为, 它们自己能够完成这些工作。


2、适用场景

装饰器模式适用于以下情况:

  • 当你需要动态地为对象添加额外的功能,而不影响其他对象。
  • 当你希望通过组合而非继承来实现对象的扩展。
  • 当你有多个不同的功能组合选项,并且想要避免创建大量的子类。

3、技术要点

装饰器模式的核心要点包括:

  • 抽象构件(Component):声明封装器和被封装对象的公用接口。

  • 具体构件(Concrete Component):是被封装对象所属的类,它定义了基础行为,但装饰类可以改变这些行为。

  • 基础装饰器 (Base Decorator) :拥有一个指向被封装对象的引用成员变量。该变量的类型应当被声明为通用部件接口,这样它就可以引用具体的部件和装饰。装饰基类会将所有操作委派给被封装的对象。

  • 具体装饰器 (Concrete Decorators):定义了可动态添加到部件的额外行为。具体装饰类会重写装饰基类的方法,并在调用父类方法之前或之后进行额外的行为。


4、案例代码

考虑一个咖啡店的订单系统,我们有不同类型的咖啡(如浓缩咖啡和拿铁咖啡),以及额外的调料(如牛奶和糖)。为了实现灵活性,我们可以使用装饰器模式来动态地为咖啡对象添加调料。


首先,我们定义抽象构件(Coffee)和具体构件(Espresso和Latte):

// 抽象构件 - 咖啡
interface Coffee {String getDescription();double getCost();
}// 具体构件 - 浓缩咖啡
class Espresso implements Coffee {@Overridepublic String getDescription() {return "浓缩咖啡";}@Overridepublic double getCost() {return 2.0;}
}// 具体构件 - 拿铁咖啡
class Latte implements Coffee {@Overridepublic String getDescription() {return "拿铁咖啡";}@Overridepublic double getCost() {return 3.0;}
}

然后,我们定义基础装饰器类(CoffeeDecorator)和具体装饰器类(MilkDecorator和SugarDecorator):

// 装饰器 - 咖啡装饰器
abstract class CoffeeDecorator implements Coffee {protected Coffee coffee;public CoffeeDecorator(Coffee coffee) {this.coffee = coffee;}@Overridepublic String getDescription() {return coffee.getDescription();}@Overridepublic double getCost() {return coffee.getCost();}
}// 具体装饰器 - 牛奶装饰器
class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return coffee.getDescription() + ",加牛奶";}@Overridepublic double getCost() {return coffee.getCost() + 0.5;}
}// 具体装饰器 - 糖装饰器
class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return coffee.getDescription() + ",加糖";}@Overridepublic double getCost() {return coffee.getCost() + 0.2;}
}

最后,我们可以使用装饰器模式来创建不同类型的咖啡,并动态地添加调料:

public class Main {public static void main(String[] args) {// 创建浓缩咖啡Coffee espresso = new Espresso();System.out.println(espresso.getDescription() + ",价格:" + espresso.getCost());// 创建拿铁咖啡Coffee latte = new Latte();System.out.println(latte.getDescription() + ",价格:" + latte.getCost());// 创建加牛奶的浓缩咖啡Coffee espressoWithMilk = new MilkDecorator(new Espresso());System.out.println(espressoWithMilk.getDescription() + ",价格:" + espressoWithMilk.getCost());// 创建加糖的拿铁咖啡Coffee latteWithSugar = new SugarDecorator(new Latte());System.out.println(latteWithSugar.getDescription() + ",价格:" + latteWithSugar.getCost());}
}

输出结果:

浓缩咖啡,价格:2.0
拿铁咖啡,价格:3.0
浓缩咖啡,加牛奶,价格:2.5
拿铁咖啡,加糖,价格:3.2

通过装饰器模式,我们可以动态地为咖啡对象添加不同的调料,而不需要修改原始的咖啡类。这种灵活性使得我们能够轻松地创建各种组合,并且可以随时添加或删除调料。


然而,装饰器模式并不仅限于咖啡店的订单系统。它在许多其他领域中都有广泛的应用,例如图形用户界面(GUI)框架、输入输出流处理等。在后续的博文中,我们将深入探讨装饰器模式的更多应用场景和技巧,让我们拭目以待!


敬请期待我们下一篇博文,将为您揭开更多关于装饰器模式的神秘面纱。


好了,今天的分享到此结束。如果觉得我的博文帮到了您,您的点赞和关注是对我最大的支持。如遇到什么问题,可评论区留言。


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

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

相关文章

【OC】使用协议(Protocol)在Cocoa应用程序中实现视图控制器之间的通信

在Cocoa应用程序开发中,视图控制器(ViewController)之间的通信是非常常见的需求。为了实现这种通信,我们可以使用协议(Protocol)来定义一个接口,然后让视图控制器遵循该协议并实现相应的方法。本…

基于单片机的超声波测距仪

收藏和点赞,您的关注是我创作的动力 文章目录 概要 一、本课题研究的主要内容二、超声波测距仪的整体方案2.2 超声波测距仪设计原理 三、超声波测距仪系统硬件电路的设计3.1 超声波测距仪的基本结构 四、 超声波测距仪系统的软件设计4.1 主程序软件设计仿真 五、结…

PTA:后序和中序构造二叉树

后序和中序构造二叉树 题目输入格式输出格式输入样例(及其对应的二叉树) 代码 题目 本题目要求用后序序列和中序序列构造一棵二叉树(树中结点个数不超过10个),并输出其先序序列。 输入格式 在第一行中输入元素个数…

leetcode做题笔记215. 数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1: 输入: [3,2,1,5,6,4], k 2…

2019数二(二重积分的不等式问题)

注&#xff1a; 1、在相同积分区域内的积分比较大小&#xff1a;被积函数大的积分值大&#xff0c;被积函数小的积分值小 2、在区间[0&#xff0c;Π/2]上 &#xff1a;sinx < x < tanx

c面向对象编码风格(上)

面向对象和面向过程的基本概念 面向对象和面向过程是两种不同的编程范式&#xff0c;它们在软件开发中用于组织和设计代码的方式。 面向过程编程&#xff08;Procedural Programming&#xff09;是一种以过程&#xff08;函数、方法&#xff09;为核心的编程方式。在面向过程…

C语言查看各数据类型所占大小

编译器&#xff1a;VC2010 #include<stdio.h> int main() {printf("%d\n",sizeof(char));printf("%d\n",sizeof(short));printf("%d\n",sizeof(int));printf("%d\n",sizeof(long));printf("%d\n",sizeof(long long))…

【sql注入】sql关卡1~4

前言&#xff1a; 靶场自取 level-1 测试注入点 POC: 1,1,1,1"",1/1,1/0 》存在注入点 爆破 POC: id-1andextractvalue(1,concat(0x7e,user(),0x7e))-- level-2 尝试注入点 POC1:admin POC2:admin POC3:adminandsleep(3)-- POC4: adminandif(1,1,0)0-- POC…

YOLOv8独家首发改进:黑夜小目标检测,ICANN会议出品,原创LEF模块,增强图像增强组成

💡本篇内容:YOLOv8独家首发改进:ICANN会议出品,黑夜小目标检测,原创LEF模块,增强图像增强组成 💡🚀🚀🚀本博客 改进源代码改进 适用于 YOLOv8 按步骤操作运行改进后的代码即可 💡本文提出改进 原创 方式:二次创新,YOLOv8专属 黑夜小目标检测论文理论部分…

【linux编程】linux文件IO的标准函数及其示例(fopen,fclose,freopen)

标准IO函数是C语言库提供的一组用于文件输入输出操作的函数,它们在stdio.h头文件中定义,可以在不同的操作系统和平台上使用,具有可移植性和简便性的优点。标准IO函数通过文件流(FILE*)来操作文件,文件流是一个结构体指针,包含了文件的信息和缓冲区,可以对文件进行缓冲和…

Flink SQL 常用作业sql

目录 flink sql常用配置kafka source to mysql sink窗口函数 开窗datagen 自动生成数据表tumble 滚动窗口hop 滑动窗口cumulate 累积窗口 grouping sets 多维分析over 函数TopN flink sql常用配置 设置输出结果格式 SET sql-client.execution.result-modetableau;kafka source…

【linux编程】linux文件IO的系统函数(close/read/fcntl/dup/dup2)

close函数 函数原型&#xff1a; #include <unistd.h> int close(int fd);参数&#xff1a;fd&#xff1a;要关闭的文件描述符 返回值&#xff1a;成功返回0&#xff0c;失败返回-1并设置errno 功能&#xff1a;关闭一个已经打开的文件&#xff0c;释放相关的资源。在…

最新 vie-vite框架下 jtopo安装使用

官方地址 官方源码 安装下载 1.官方好像都没有给git地址&#xff0c;尝试npm安装报错 2.找到1.0.5之前的版本npm i jtopo2&#xff0c;安装成功后使用报错&#xff0c;应该是版本冲突了 1.本地引入&#xff0c; 点击官方源码下载&#xff0c;需要jtopo_npm文件 2.引入到本…

Jetpack:030-Jetpack中的状态

文章目录 1. 概念介绍2. 使用方法2.1 可监听对象2.2 获取状态值2.3 修改状态值2.4 重组函数 3. 示例代码4. 内容总结 我们在上一章回中介绍了Jetpack中网格布局相关的内容&#xff0c;本章回中主要 介绍状态。闲话休提&#xff0c;让我们一起Talk Android Jetpack吧&#xff0…

【SpringCloud Alibaba -- Nacos】Linux 搭建 Nacos 集群

搭建 Nacos 集群 架构 centos安装docker https://docs.docker.com/engine/install/centos/ 详细配置过程 MySql8 mysql数据库配置 数据库脚本 nacos/conf/nacos-mysql.sql Nacos2 application.properties 修改为mysql spring.datasource.platformmysqldb.num1 db.url…

【工具】Github统计代码行数工具推荐(VScode插件、兼容任何平台、不用下载安装包)

需求&#xff1a; 1&#xff09;被要求统计代码行数&#xff1b; 2&#xff09;不想打开Linux&#xff0c;懒得下载Windows版本GitStats&#xff1b; 3&#xff09;打开了Linux但也不记得find命令行怎么用&#xff1b; 4&#xff09;打开了Linux&#xff0c;装好了Gitstats但自…

wagtail的使用

文章目录 安装虚拟环境新建项目时指定虚拟环境打开已有项目添加虚拟环境 安装wagtail查看安装后的包 创建wagtail项目安装依赖迁移创建超级用户运行项目 管理工作台内容扩展首页的数据模型更新数据库修改模板页创建一个页面的过程 models中的基本字段templates字符型文本字段富…

汽车标定技术(四)--问题分析:多周期测量时上位机显示异常

目录 1.问题现象 2.数据流分析 ​​​​3.代码分析 3.1 AllocDAQ 3.2 AllocOdt 3.3 AllocOdtEntry 4.根因分析及解决方法 4.1 根因分析 4.2 解决方案 1.问题现象 在手撸XCP代码时&#xff0c; DAQ的实现是一大头痛的事情。最初单周期实现还好一点&#xff0c;特别是…

MATLAB - Gazebo 联合仿真 —— 使用 UR10 机械臂检测和采摘水果

系列文章目录 文章目录 系列文章目录前言一、设置 Gazebo 仿真环境二、在 Gazebo 中模拟和控制机器人2.1 概述2.2 任务调度器2.3 感知和目标生成系统2.4 运动规划2.5 机械臂和关节控制系统 三、分配用于控制机器人的参数3.1 定义机器人模型和运动规划参数&#xff0c;3.2 定义机…

OpenCV(应用) —— 目标轮廓的相关应用

文章目录 一、目标轮廓的获取与绘制二、轮廓的信息&#xff08;面积和周长&#xff09;三、轮廓外接形状的三种表达方式 一、目标轮廓的获取与绘制 通常&#xff0c;使用findContours() 函数是为了获取一张图像内目标对象的所有轮廓&#xff0c;并且在 OpenCV4.x 版本中&#…