c++常见设计模式之装饰器模式

ops/2025/1/24 6:53:03/

基础介绍

装饰器模式是结构型设计模式,从字面意思看装饰器设计模式就是用来解决在原有的实现基础上添加一些额外的实现的问题。那么正统的概念是什么呢?装饰器模式允许我们动态的向对象添加新的 行为,同时不改变其原有的结构。它是一种比继承更灵活的扩展对象功能的方式。

举个简单的例子,比如手机作为一个产品,希望在基础手机的基础上实现新增两个功能1,且不希望改变类原有的结构,这种情况下就需要使用到装饰器模式

实现原理

装饰器模式的实现原理:继承+虚函数的实现方式。典型的结构如下:

//基础的组件接口
class Component
{public:virtual std::string operation() = 0;  //纯虚函数,作为接口使用
}//具体组件
class concreteComponent:public Component
{public:concreteComponent(Component* component):Component(component){}std::string operation(){return "concreteComponent";}
}//装饰器基类
class Decorator: public Component
{public:Component* component_;  //该成员必须存在std::string operation() const override{return component_->operation();}
}
//具体的装饰器类A
class concreteDecoratorA: public Decorator
{public:concreteDecoratorA(Component* component):Decorator(component){}std::string operation() const override{reutrn "concreteDecoratorA " + Decorator::operation();}
}//具体的装饰器类B
class concreteDecoratorB: public Decorator
{public:concreteDecoratorA(Component* component):Decorator(component){}std::string operation() const override{reutrn "concreteDecoratorB " + Decorator::operation();}
}

 原理解析:

  • 需要设计一个接口类,该接口定义了对象行为的基本接口,接口为纯虚函数
  • 需要一个基于该接口类的具体类,该具体类是一个具体的实现。
  • 需要设计一个装饰器基类,该基类同样的是一个接口类的一个具体实现。装饰器基类的具体功能是调用实际
  • 1个或者多个装饰器子类,在各个装饰器子类中实现了需要对原有接口进行动态添加行为的具体实现。

具体装饰器实例

下面的例子是一个具体的装饰器的例子:

#include <iostream>
#include <memory>
// 使用装饰器模式
class Coffee {
public:virtual double cost() = 0;virtual std::string description() = 0;virtual ~Coffee() = default;
};class SimpleCoffee : public Coffee {
public:double cost() override { return 10.0; }std::string description() override { return "Simple Coffee"; }
};// 装饰器基类
class CoffeeDecorator : public Coffee {
protected:std::unique_ptr<Coffee> coffee_;public:CoffeeDecorator(std::unique_ptr<Coffee> coffee): coffee_(std::move(coffee)) {}double cost() override { return coffee_->cost(); }std::string description() override { return coffee_->description(); }
};// 具体装饰器
class MilkDecorator : public CoffeeDecorator {
public:MilkDecorator(std::unique_ptr<Coffee> coffee): CoffeeDecorator(std::move(coffee)) {}double cost() override {return CoffeeDecorator::cost() + 5.0;}std::string description() override {return CoffeeDecorator::description() + " with milk";}
};class SugarDecorator : public CoffeeDecorator {
public:SugarDecorator(std::unique_ptr<Coffee> coffee): CoffeeDecorator(std::move(coffee)) {}double cost() override {return CoffeeDecorator::cost() + 2.0;}std::string description() override {return CoffeeDecorator::description() + " with sugar";}
};class ChocolateDecorator : public CoffeeDecorator {
public:ChocolateDecorator(std::unique_ptr<Coffee> coffee): CoffeeDecorator(std::move(coffee)) {}double cost() override {return CoffeeDecorator::cost() + 3.0;}std::string description() override {return CoffeeDecorator::description() + " with chocolate";}
};// 使用示例
int main() {// 创建一个简单的咖啡std::unique_ptr<Coffee> coffee = std::make_unique<SimpleCoffee>();std::cout << coffee->description() << ": $" << coffee->cost() << std::endl;// 加牛奶coffee = std::make_unique<MilkDecorator>(std::move(coffee));std::cout << coffee->description() << ": $" << coffee->cost() << std::endl;// 加糖coffee = std::make_unique<SugarDecorator>(std::move(coffee));std::cout << coffee->description() << ": $" << coffee->cost() << std::endl;// 再加巧克力coffee = std::make_unique<ChocolateDecorator>(std::move(coffee));std::cout << coffee->description() << ": $" << coffee->cost() << std::endl;return 0;
}

代码运行解析

有了上面的代码,很多但是很多朋友对这个调用的过程不是很了解下面详细解析资源的转移、函数的调用:

  • 初始状态及加牛奶
// 初始创建
std::unique_ptr<Coffee> coffee = std::make_unique<SimpleCoffee>();
/* 内存状态:
coffee (unique_ptr) ---> SimpleCoffee对象
*/// 输出初始状态
std::cout << coffee->description() << ": $" << coffee->cost() << std::endl;
/* 函数调用过程:
1. coffee->description() 直接调用 SimpleCoffee::description()
2. coffee->cost() 直接调用 SimpleCoffee::cost()
*/// 加牛奶
coffee = std::make_unique<MilkDecorator>(std::move(coffee));
/* 执行过程:
1. std::move(coffee) 将原始coffee转为右值引用
2. MilkDecorator构造函数接收这个右值引用
3. 调用基类CoffeeDecorator构造函数
4. 原始coffee指针被存储在MilkDecorator的coffee_成员中
5. 原始coffee被置为nullptr
6. 新的unique_ptr指向MilkDecorator对象最终内存状态:
coffee (unique_ptr) ---> MilkDecorator对象|+---> coffee_ (unique_ptr) ---> SimpleCoffee对象
*/
  • 加糖过程
// 加糖
coffee = std::make_unique<SugarDecorator>(std::move(coffee));
/* 执行过程:
1. std::move(coffee) 将指向MilkDecorator的coffee转为右值引用
2. SugarDecorator构造函数接收这个右值引用
3. 调用基类CoffeeDecorator构造函数
4. MilkDecorator对象的指针被存储在SugarDecorator的coffee_成员中
5. 原始coffee被置为nullptr
6. 新的unique_ptr指向SugarDecorator对象最终内存状态:
coffee (unique_ptr) ---> SugarDecorator对象|+---> coffee_ (unique_ptr) ---> MilkDecorator对象|+---> coffee_ (unique_ptr) ---> SimpleCoffee对象
*/// 输出当前状态
std::cout << coffee->description() << ": $" << coffee->cost() << std::endl;
/* 函数调用过程:
description() 调用链:
1. SugarDecorator::description() -> CoffeeDecorator::description() -> MilkDecorator::description()-> CoffeeDecorator::description()-> SimpleCoffee::description()cost() 调用链:
1. SugarDecorator::cost()-> CoffeeDecorator::cost()-> MilkDecorator::cost()-> CoffeeDecorator::cost()-> SimpleCoffee::cost()
*/
  • 加巧克力过程
// 加巧克力
coffee = std::make_unique<ChocolateDecorator>(std::move(coffee));
/* 执行过程:
1. std::move(coffee) 将指向SugarDecorator的coffee转为右值引用
2. ChocolateDecorator构造函数接收这个右值引用
3. 调用基类CoffeeDecorator构造函数
4. SugarDecorator对象的指针被存储在ChocolateDecorator的coffee_成员中
5. 原始coffee被置为nullptr
6. 新的unique_ptr指向ChocolateDecorator对象最终内存状态:
coffee (unique_ptr) ---> ChocolateDecorator对象|+---> coffee_ (unique_ptr) ---> SugarDecorator对象|+---> coffee_ (unique_ptr) ---> MilkDecorator对象|+---> coffee_ (unique_ptr) ---> SimpleCoffee对象
*/// 输出最终状态
std::cout << coffee->description() << ": $" << coffee->cost() << std::endl;
/* 函数调用链:
description() 调用链:
1. ChocolateDecorator::description()-> CoffeeDecorator::description()-> SugarDecorator::description()-> CoffeeDecorator::description()-> MilkDecorator::description()-> CoffeeDecorator::description()-> SimpleCoffee::description()cost() 调用链:
1. ChocolateDecorator::cost()-> CoffeeDecorator::cost()-> SugarDecorator::cost()-> CoffeeDecorator::cost()-> MilkDecorator::cost()-> CoffeeDecorator::cost()-> SimpleCoffee::cost()
*/
  • 资源释放过程
// 当coffee离开作用域时,析构顺序:
1. 首先析构ChocolateDecorator
2. 然后析构SugarDecorator
3. 接着析构MilkDecorator
4. 最后析构SimpleCoffee/* 析构过程是一个递归的过程:
1. ~ChocolateDecorator() -> 析构其coffee_成员(SugarDecorator)-> ~SugarDecorator()-> 析构其coffee_成员(MilkDecorator)-> ~MilkDecorator()-> 析构其coffee_成员(SimpleCoffee)-> ~SimpleCoffee()
*/


http://www.ppmy.cn/ops/152675.html

相关文章

Node.js日志记录新篇章:morgan中间件的使用与优势

在Node.js的广阔生态系统中&#xff0c;日志记录是开发过程中不可或缺的一部分。它不仅有助于开发者追踪应用程序的运行状态&#xff0c;还能在出现问题时提供宝贵的调试信息。而在众多日志记录工具中&#xff0c;Morgan以其高效、易用和专注于HTTP请求日志的特点&#xff0c;成…

spring cloud如何实现负载均衡

在Spring Cloud中&#xff0c;实际上并没有直接支持lb:\\这样的URL前缀来自动解析为负载均衡的服务地址。lb:\\这样的表示可能是在某些特定框架、文档或示例中自定义的&#xff0c;但它并不是Spring Cloud官方API或规范的一部分。 Spring Cloud实现负载均衡的方式通常依赖于服…

基于微信小程序的手机银行系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

Java 中多态与接口的全面解析

Java学习资料 Java学习资料 Java学习资料 在 Java 编程世界里&#xff0c;多态与接口是两个极为重要的概念&#xff0c;它们为开发者构建灵活、可扩展且易于维护的程序提供了强大的支持。深入理解这两个概念及其相互关系&#xff0c;对于提升 Java 编程能力至关重要。 一、多…

excel批量提取批注

打开excel ALTF11 ​​​​​​​ ​​​​​​​ 插入代码 Function GetComment(rng As Range) As StringOn Error Resume NextDim commentText As StringcommentText rng.Comment.TextcommentText Replace(commentText, "rina.farriani:", "")GetC…

数字人+虚拟展厅:开启互动展览新篇章!

“数字人展厅”这一组合正逐渐成为展览展示领域的新宠&#xff0c;它融合了最前沿的人工智能、虚拟现实、增强现实等技术&#xff0c;为观众带来了前所未有的互动新体验。 数字人&#xff0c;即利用计算机图形学、人工智能等技术生成的具有人类外貌、行为和交互能力的虚拟形象…

MYSQL学习笔记(五):单行函数(字符串、数学、日期时间、条件判断、信息、加密、进制转换函数)讲解

前言&#xff1a; 学习和使用数据库可以说是程序员必须具备能力&#xff0c;这里将更新关于MYSQL的使用讲解&#xff0c;大概应该会更新30篇&#xff0c;涵盖入门、进阶、高级(一些原理分析);这一篇是讲解单行函数&#xff0c;当然mysql函数很多哈&#xff0c;只有多用才能记得…

单片机数码管动态显示

在学习 51 单片机的过程中&#xff0c;数码管动态显示是一个非常基础且重要的知识点。通过数码管&#xff0c;我们可以直观地展示数字、字符等信息&#xff0c;在很多电子设备中都有广泛应用&#xff0c;比如电子时钟、数字万用表等。本文将详细介绍 51 单片机数码管动态显示的…