当前位置: 首页 > news >正文

C++设计模式——Decorator装饰器模式

一,装饰器模式简介

装饰器模式是一种结构型设计模式, 它允许在不改变现有对象的情况下,动态地将功能添加到对象中。

装饰器模式是通过创建具有新行为的对象来实现的,这些对象将原始对象进行了包装。

装饰器模式遵循开放/关闭原则,允许开发者在不修改现有代码的情况下添加新的装饰器。

日常开发中常用的装饰器属于类装饰器,通过继承父类来实现。

二,装饰器模式的结构

1.抽象组件(Component):被装饰的对象,声明了对外的统一接口。

2.具体组件(ConcreteComponent):包含抽象组件接口的具体代码实现。

3.抽象装饰器(Decorator):包含对抽象组件的指针或引用,并定义了与抽象组件一致的接口。

4.具体装饰器(ConcreteDecorator):包含抽象装饰器接口的具体代码实现,并且可以在调用对外接口之前或之后添加额外的行为。

对应UML类图:

拿生活中举例:某硬核产品的可拆卸装饰

三,装饰器模式的工作步骤

1.创建抽象组件类,定义抽象组件的对外接口,将核心功能声明在该接口中。

2.创建具体组件类,继承抽象组件类,实现抽象组件类的接口。

3.创建抽象装饰器类,继承抽象组件类,实现抽象组件类的接口,并持有一个抽象组件对象的引用。

4.创建具体装饰器类,继承抽象装饰器类,在实现核心接口之后,添加额外的接口函数。

5.在客户端中使用装饰器包装抽象组件,并调用它们的方法。

四,装饰器模式代码样例

暂时不带具体装饰器ConcreteDecorator,直接在Decorator中添加额外功能。

#include <iostream>class Component {
public:virtual void operation() = 0;
};class ConcreteComponent : public Component {
public:void operation() override {std::cout << "From ConcreteComponent." << std::endl;}
};class Decorator : public Component {
public:Decorator(Component& component):component_(component){}void operation() override {std::cout << "from Decorator." << std::endl;component_.operation();}private:Component& component_;
};int main() {ConcreteComponent concreteComponent;Decorator decoratedComponent(concreteComponent);decoratedComponent.operation();return 0;
}

运行结果:

from Decorator.
From ConcreteComponent.

五,装饰器模式的应用场景

组件扩展:在大型项目中,随着业务的增加,必定要添加新的功能,装饰器此时可以避免修改原有的基础组件。

API增强:当提供API给第三方进行调用时,装饰器可以用于添加额外的功能,比如日志记录、安全校验等,而调用者无需知道具体的细节。

权限管理:装饰器可以用来控制对原有的特定接口的访问权限。

缓存机制:在网络请求或数据库查询等操作中,装饰器可以用来添加额外的缓存、重试、超时处理等功能。

六,装饰器模式的优缺点

装饰器模式的优点:

1.可以动态地添加或删除对象的功能,无需修改原有的代码。

2.不影响现有对象的结构,符合开闭原则。

3.可以灵活地扩展原有对象的功能。

4.可以使用多个装饰器对象来组合多种功能。

5.使得代码可以根据需要轻松地添加或移除功能。

装饰器模式的缺点:

1.使系统中增加额外的类变量。

2.装饰器对象与原始对象之间的关系过于复杂,降低代码可读性。

七,代码实战

Demo1: 自助冰淇淋制作机

#include <iostream>
#include <string>using namespace std;// Component
class IceCream {
public:virtual string getDescription() const = 0;virtual double cost() const = 0;
};// Concrete Component
class VanillaIceCream : public IceCream {
public:string getDescription() const override{return "Vanilla Ice Cream";}double cost() const override { return 160.0; }
};// Decorator
class Decorator : public IceCream {
protected:IceCream* iceCream;public:Decorator(IceCream* ic): iceCream(ic){}string getDescription() const override{return iceCream->getDescription();}double cost() const override{return iceCream->cost();}
};// Concrete Decorator - adds chocolate topping.
class ChocolateDecorator : public Decorator {
public:ChocolateDecorator(IceCream* ic): Decorator(ic){}string getDescription() const override{return iceCream->getDescription()+ " with Chocolate";}double cost() const override{return iceCream->cost() + 100.0;}
};// Concrete Decorator - adds caramel topping.
class CaramelDecorator : public Decorator {
public:CaramelDecorator(IceCream* ic): Decorator(ic){}string getDescription() const override{return iceCream->getDescription() + " with Caramel";}double cost() const override{return iceCream->cost() + 150.0;}
};int main()
{// Create a vanilla ice creamIceCream* vanillaIceCream = new VanillaIceCream();cout << "Order: " << vanillaIceCream->getDescription()<< ", Cost: Rs." << vanillaIceCream->cost()<< endl;// Wrap it with ChocolateDecoratorIceCream* chocolateIceCream= new ChocolateDecorator(vanillaIceCream);cout << "Order: " << chocolateIceCream->getDescription()<< ", Cost: Rs." << chocolateIceCream->cost()<< endl;// Wrap it with CaramelDecoratorIceCream* caramelIceCream= new CaramelDecorator(chocolateIceCream);cout << "Order: " << caramelIceCream->getDescription()<< ", Cost: Rs." << caramelIceCream->cost()<< endl;delete vanillaIceCream;delete chocolateIceCream;delete caramelIceCream;return 0;
}

运行结果:

Order: Vanilla Ice Cream, Cost: Rs.160
Order: Vanilla Ice Cream with Chocolate, Cost: Rs.260
Order: Vanilla Ice Cream with Chocolate with Caramel, Cost: Rs.410

Demo2: 模拟的绘图组件

#include <iostream>
using namespace std;// Component
class Widget
{
public:virtual void draw() = 0;
};// Concrete Component
class TextField : public Widget
{int width, height;
public:TextField(int w, int h){width = w;height = h;}void draw(){cout << "TextField: " << width << "," << height;}
};// Decorator
class Decorator : public Widget  
{Widget* wid;
public:Decorator(Widget* w){wid = w;}void draw(){wid->draw();}
};// Concrete Decorator
class BorderDecorator : public Decorator
{
public:BorderDecorator(Widget* w): Decorator(w){}void draw(){//基础功能Decorator::draw();//扩展功能cout << "\n BorderDecorator" ;}
};// Concrete Decorator
class ScrollDecorator : public Decorator
{
public:ScrollDecorator(Widget* w): Decorator(w){}/*virtual*/void draw(){//基础功能Decorator::draw();//扩展功能cout << "\n ScrollDecorator";}
};
int main()
{Widget* aWidget = new BorderDecorator(new ScrollDecorator(new TextField(80, 24)));aWidget->draw();
}

运行结果:

TextField: 80,24
ScrollDecorator
BorderDecorator

八,参考阅读

https://www.pentalog.com/blog/design-patterns/decorator-design-pattern/

https://www.geeksforgeeks.org/introduction-to-decorator-pattern-in-c-design-patterns/

https://sourcemaking.com/design_patterns/decorator/cpp/2

https://sourcemaking.com/design_patterns/decorator

相关文章:

  • OpenCV 4.10 发布
  • SpringBoot使用jasypt实现数据库信息的脱敏,以此来保护数据库的用户名username和密码password(容易上手,详细)
  • 经销商的生意好坏很大程度上跟这群人有关
  • 利用Cesium和JS实现地点点聚合功能
  • FastWeb - Lua开源跨平台网站开发服务
  • Swift 是 C++ 的最佳继任者
  • OpenCore 引导完美升级
  • JAVA开发 选择指定的文件生成ZIP压缩包
  • 媒体查询的屏幕尺寸范围
  • 中文版svn怎么忽略文件
  • MFC工控项目实例之三theApp变量传递对话框参数
  • Java 异常处理 -- Java 语言的异常、异常链与断言
  • react修改本地运行项目的端口
  • 重装系统,以及设置 深度 学习环境
  • Kubernetes(K8s)从入门到精通系列之十九:Operator模式
  • 【译】理解JavaScript:new 关键字
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • JAVA SE 6 GC调优笔记
  • javascript从右向左截取指定位数字符的3种方法
  • Java方法详解
  • Logstash 参考指南(目录)
  • python docx文档转html页面
  • python3 使用 asyncio 代替线程
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • storm drpc实例
  • 从PHP迁移至Golang - 基础篇
  • ------- 计算机网络基础
  • 为什么要用IPython/Jupyter?
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • #android不同版本废弃api,新api。
  • #include<初见C语言之指针(5)>
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (7)svelte 教程: Props(属性)
  • (C#)获取字符编码的类
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (转)甲方乙方——赵民谈找工作
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • .NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库
  • .NET MVC之AOP
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .NET 动态调用WebService + WSE + UsernameToken
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .NET面试题解析(11)-SQL语言基础及数据库基本原理
  • .NET学习教程二——.net基础定义+VS常用设置
  • .NET中 MVC 工厂模式浅析
  • .net专家(张羿专栏)
  • .pyc文件还原.py文件_Python什么情况下会生成pyc文件?
  • .vimrc 配置项
  • //解决validator验证插件多个name相同只验证第一的问题
  • [ 环境搭建篇 ] 安装 java 环境并配置环境变量(附 JDK1.8 安装包)
  • [④ADRV902x]: Digital Filter Configuration(发射端)