装饰模式(Decorator Pattern)

定义

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许通过将对象包装在装饰器类的实例中来动态地添加新的行为和责任。这种模式可以在不修改现有代码的情况下,灵活地扩展对象的功能。

示例

考虑一个咖啡店的场景,有不同种类的咖啡,你可以选择添加不同的配料,比如牛奶、糖和巧克力。使用装饰模式可以动态地为咖啡添加不同的配料,而不需要修改咖啡类的代码。

类结构

  1. Component(组件): 定义了一个抽象接口,用于具体组件和装饰器共享。
  2. ConcreteComponent(具体组件): 实现了Component接口的具体类,是被装饰的对象。
  3. Decorator(装饰器): 也实现了Component接口,并持有一个Component对象的引用,这是装饰的核心。
  4. ConcreteDecorator(具体装饰器): 扩展了Decorator类,负责具体的装饰操作。

代码实现

#include <iostream>
#include <string>// Step 1: Component(组件)
class Coffee {
public:virtual std::string getDescription() const = 0;virtual double cost() const = 0;
};// Step 2: ConcreteComponent(具体组件)
class SimpleCoffee : public Coffee {
public:std::string getDescription() const override {return "简单咖啡";}double cost() const override {return 5.0;}
};// Step 3: Decorator(装饰器)
class CoffeeDecorator : public Coffee {
protected:Coffee* coffee;public:CoffeeDecorator(Coffee* c) : coffee(c) {}std::string getDescription() const override {return coffee->getDescription();}double cost() const override {return coffee->cost();}
};// Step 4: ConcreteDecorator(具体装饰器)
class MilkDecorator : public CoffeeDecorator {
public:MilkDecorator(Coffee* c) : CoffeeDecorator(c) {}std::string getDescription() const override {return coffee->getDescription() + ",加牛奶";}double cost() const override {return coffee->cost() + 2.0;}
};class SugarDecorator : public CoffeeDecorator {
public:SugarDecorator(Coffee* c) : CoffeeDecorator(c) {}std::string getDescription() const override {return coffee->getDescription() + ",加糖";}double cost() const override {return coffee->cost() + 1.0;}
};int main() {// 创建一个简单咖啡Coffee* myCoffee = new SimpleCoffee();std::cout << "描述:" << myCoffee->getDescription() << ",价格:" << myCoffee->cost() << "元" << std::endl;// 使用装饰器动态添加牛奶myCoffee = new MilkDecorator(myCoffee);std::cout << "描述:" << myCoffee->getDescription() << ",价格:" << myCoffee->cost() << "元" << std::endl;// 使用装饰器再添加糖myCoffee = new SugarDecorator(myCoffee);std::cout << "描述:" << myCoffee->getDescription() << ",价格:" << myCoffee->cost() << "元" << std::endl;// 释放内存delete myCoffee;return 0;
}

结构图示

在这个示例中,Coffee是组件,SimpleCoffee是具体组件,CoffeeDecorator是装饰器,MilkDecoratorSugarDecorator是具体装饰器。通过不同的组合,我们可以动态地扩展咖啡的描述和价格,而无需修改原始的咖啡类。这就是装饰模式

适用场景

动态地添加或修改对象的功能

当需要动态地为一个对象添加额外的功能,而且希望这些功能可以灵活组合时,装饰模式是一个很好的选择。这样可以避免使用大量子类来实现所有可能的组合,而是使用装饰器来动态地添加这些功能。


避免使用继承导致的类爆炸

经常会发现在类的层次结构中添加新功能导致的子类爆炸问题。装饰模式通过将功能分离到单独的装饰器类中,避免了这种情况的发生。


保持类的简单性和单一责任原则

使用装饰模式可以将一些复杂的功能分离到单独的装饰器类中,使得原始类保持简单和具有单一职责。

在运行时动态地添加或删除功能

装饰模式允许在运行时动态地添加或删除对象的功能,这对于某些情况下的配置和扩展非常有用。

经典使用方案


Java I/O库中的输入输出流

 Java中的输入输出流就是一个典型的装饰器模式的例子。基本的InputStream或OutputStream可以通过添加额外的功能,比如缓冲、加密或压缩等,而无需修改它们的代码。


GUI界面组件

 在GUI编程中,经常需要动态地添加新的功能或外观到用户界面组件上。比如,一个简单的文本框可以通过装饰模式来添加滚动条、边框、背景色等功能,而无需修改原始文本框类的代码。


 Web开发中的过滤器

 在Web开发中,过滤器常常用于对请求或响应进行处理,比如身份验证、日志记录、数据压缩等。使用装饰模式可以轻松地添加新的过滤功能,同时保持代码的灵活性和可维护性。


这些都是装饰模式在实际应用中的经典场景

示例说明使用装饰模式的好处

#include <iostream>
#include <memory>// 抽象组件
class Component {
public:virtual void operation() = 0;virtual ~Component() {}
};// 具体组件
class ConcreteComponent : public Component {
public:virtual void operation() override {std::cout << "ConcreteComponent operation\n";}
};// 抽象装饰器
class Decorator : public Component {
protected:std::shared_ptr<Component> component;public:Decorator(std::shared_ptr<Component> comp) : component(comp) {}virtual void operation() override {if (component != nullptr) {component->operation();}}
};// 具体装饰器 A
class ConcreteDecoratorA : public Decorator {
public:ConcreteDecoratorA(std::shared_ptr<Component> comp) : Decorator(comp) {} //这里初始化了基类 virtual void operation() override {Decorator::operation();// 调用了基类的public函数addedBehavior();}void addedBehavior() {std::cout << "Added Behavior by ConcreteDecoratorA\n";}
};// 具体装饰器 B
class ConcreteDecoratorB : public Decorator {
public:ConcreteDecoratorB(std::shared_ptr<Component> comp) : Decorator(comp) {}virtual void operation() override {Decorator::operation();addedState();}void addedState() {std::cout << "Added State by ConcreteDecoratorB\n";}
};int main() {// 创建具体组件对象std::shared_ptr<Component> component = std::make_shared<ConcreteComponent>();// 添加装饰器 Astd::shared_ptr<Component> decoratedA = std::make_shared<ConcreteDecoratorA>(component);decoratedA->operation();std::cout << "------\n";// 添加装饰器 Bstd::shared_ptr<Component> decoratedB = std::make_shared<ConcreteDecoratorB>(component);decoratedB->operation();std::cout << "------\n";// 动态组合装饰器 A 和 Bstd::shared_ptr<Component> decoratedAB = std::make_shared<ConcreteDecoratorA>(decoratedB);decoratedAB->operation();return 0;
}

在这个示例中,我们有一个抽象组件 Component,它定义了操作的接口。然后有一个具体的组件 ConcreteComponent 实现了这个接口。然后我们有一个抽象装饰器 Decorator,它也实现了 Component 接口,并持有一个指向 Component 对象的指针。具体的装饰器类 ConcreteDecoratorAConcreteDecoratorB 继承自 Decorator,并添加了额外的行为或状态。

main() 函数中,我们可以看到如何动态地添加或组合装饰器。通过创建具体组件对象,然后将其传递给不同的装饰器来添加不同的行为或状态。这展示了装饰模式的灵活性和动态性。

编译运行

分析

动态地添加或修改对象的功能

这里实现了灵活添加组件

// 添加装饰器 A
    std::shared_ptr<Component> decoratedA = std::make_shared<ConcreteDecoratorA>(component);
    decoratedA->operation();

避免使用继承导致的类爆炸

 上述例子,通过组件通过添加装饰类的方式,将装饰功能分离,而不需要使用继承或者组合的方式,解耦了功能的扩展,新的装饰添加时,不需要改动组件,只要添加新的装饰类即可,避免了需要不断继承带来的风险和大量继承类。

保持类的简单性和单一责任原则

这里通过将装饰功能的分离,保持类的简单性和单一责任原则。

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

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

相关文章

springboot集成mqtt

文章目录 前言一、MQTT是什么&#xff1f;二、继承步骤1.安装MQTT2.创建项目&#xff0c;引入依赖3. 对应步骤2的代码3 测试 总结mqtt 启动后访问地址 前言 随着物联网的火热,MQTT的应用逐渐增多 曾经也有幸使用过mqtt,今天正好总结下MQTT的使用; 一、MQTT是什么&#xff1f;…

[OpenAI]继ChatGPT后发布的Sora模型原理与体验通道

前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言OpenAI体验通道Spacetime Latent Patches 潜变量时空碎片, 建构视觉语言系统…

unity学习(28)——登录功能

有之前注册的知识&#xff0c;登录就很容易处理了。 登陆成功返回id&#xff1a; 登录失败返回null&#xff1a; 测试同一账号不能重复登陆&#xff01;登录成功后最好可以跳到新的场景中 结果是好的&#xff0c;去服务器看一下对应部分的代码&#xff0c;可见&#xff0c;登…

java面向对象上:类的结构之一

目录 1.相同点 2.不同点 2.1 在类中声明的位置的不同 2.2 关于权限修饰符的不同 2.3 默认初始化值的情况&#xff1a; 2.4 在内存中加载的位置 补充&#xff1a;回顾变量的分类&#xff1a; 方式一&#xff1a;按照数据类型&#xff1a; 方式二&#xff1a;按照在类中…

springboot211基于springboot医疗报销系统的设计与实现

医疗报销系统的设计与实现 摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;报销单信息因为其管理内容繁杂&#xff0c;管理数量繁多…

备战蓝桥杯---基础算法刷题1

最近在忙学校官网上的题&#xff0c;就借此记录分享一下有价值的题&#xff1a; 1.注意枚举角度 如果我们就对于不同的k常规的枚举&#xff0c;复杂度直接炸了。 于是我们考虑换一个角度&#xff0c;我们不妨从1开始枚举因子&#xff0c;我们记录下他的倍数的个数sum个&#…

华为算法题 go语言或者ptython

1 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返…

【java面试系列】服务的限流

目录 一、常用的限流算法1.固定窗口计数器(计数器算法)2 滑动窗口计数器算法3. 漏桶算法4 令牌桶算法(`常用`)Google开源项目Guava中的RateLimiter使用的就是令牌桶控制算法二、 分布式限流1、网关层(Nginx、Openresty、Spring Cloud Gateway等)流量限制nginx限流Spring Cl…

C# CAD2016 cass10宗地Xdata数据写入

一、 查看cass10写入信息 C# Cad2016二次开发获取XData信息&#xff08;二&#xff09; 一共有81条数据 XData value: QHDM XData value: 121321 XData value: SOUTH XData value: 300000 XData value: 141121JC10720 XData value: 权利人 XData value: 0702 XData value: YB…

【算法与数据结构】200、695、LeetCode岛屿数量(深搜+广搜) 岛屿的最大面积

文章目录 一、200、岛屿数量1.1 深度优先搜索DFS1.2 广度优先搜索BFS 二、695、岛屿的最大面积2.1 深度优先搜索DFS2.2 广度优先搜索BFS 三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、200、岛屿数量 1.1 深度优先搜…

1.CSS单位总结

CSS 单位总结 经典真题 px 和 em 的区别 CSS 中的哪些单位 首先&#xff0c;在 CSS 中&#xff0c;单位分为两大类&#xff0c;绝对长度单位和相对长度单位。 绝对长度单位 我们先来说这个&#xff0c;绝对长度单位最好理解&#xff0c;和我们现实生活中是一样的。在我们…

Django——ORM增删改查

基本对象 model.objects 创建数据 可以通过django编写的命令行方式快捷创建数据 python manage.py shell 如果对模型层有任何修改都需要重启shell&#xff0c;否则操作容易出错 在shell中我们需要先引入我们的模型&#xff0c;如from bookstore.models import Book 然后通过…

网络安全-nc(Netcat)工具详解

经常在反弹shell的时候使用nc命令&#xff0c;但是从来没有了解过&#xff0c;今天翻书看到了&#xff0c;准备记录一下。 nc全称Netcat&#xff0c;是TCP/IP连接的瑞士军刀。哈哈我最喜欢瑞士军刀了。 有一个比较偏的知识点&#xff0c;nc还可以探测目标的端口是否开放&…

个人博客系列-项目部署-nginx(3)

使用Nginx uwsgi进行部署django项目 一. 检查项目是否可以运行 启动项目 python manage.py runserver 0.0.0.0:8099输入ip:8099 查看启动页面 出现上述页面表示运行成功 二. 安装uwsgi并配置 2.1 下载uwsgi pip install uwsgi新建文件test.py写入内容&#xff0c;测试一…

Druid无法登录监控页面

问题表现&#xff1a;在配置和依赖都正确的情况下&#xff0c;无法通过配置的用户名密码登录Druid的监控页面 检查配置发现 配置的用户名和密码和请求中参数是一致的&#x1f914; Debug发现 ResourceServlet 是Druid的登录实现&#xff0c; 且调试发现usernameParam是null&am…

http相关概念以及apache的功能(最详细讲解!!!!)

概念 互联网&#xff1a;是网络的网络&#xff0c;是所有类型网络的母集 因特网&#xff1a;世界上最大的互联网网络 万维网&#xff1a;www &#xff08;不是网络&#xff0c;而是数据库&#xff09;是网页与网页之间的跳转关系 URL:万维网使用统一资源定位符&#xff0c;…

ES6 | (一)ES6 新特性(上) | 尚硅谷Web前端ES6教程

文章目录 &#x1f4da;ES6新特性&#x1f4da;let关键字&#x1f4da;const关键字&#x1f4da;变量的解构赋值&#x1f4da;模板字符串&#x1f4da;简化对象写法&#x1f4da;箭头函数&#x1f4da;函数参数默认值设定&#x1f4da;rest参数&#x1f4da;spread扩展运算符&a…

目标检测卷王YOLO卷出新高度:YOLOv9问世

论文摘要:如今的深度学习方法重点关注如何设计最合适的目标函数,使得模型的预测结果能够最接近真实情况。 同时,必须设计一个适当的架构,可以帮助获取足够的信息进行预测。 现有方法忽略了一个事实,即当输入数据经过逐层特征提取和空间变换时,大量信息将会丢失。 本文将深…

腾讯云宝塔Linux安装Mysql5.7

一、下载官方mysql包 wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm二、安装mysql包 rpm -ivh mysql-community-release-el7-5.noarch.rpm三、安装mysql yum install mysql-community-server -y四、启动数据库 systemctl start mysqld.service…

力扣算法Algorithm竞赛模板库(codeforces-go):含了算法竞赛中常用的数据结构和算法实现,助力开发者更高效地解决问题

1.算法Algorithm竞赛模板库&#xff08;codeforces-go&#xff09; 算法竞赛模板库&#xff0c;为算法竞赛爱好者提供了一系列精心设计的算法模板。这个库包含了算法竞赛中常用的数据结构和算法实现&#xff0c;助力开发者更高效地解决问题 一个算法模板应当涵盖以下几点&…