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

基础介绍

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

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

实现原理

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

cpp 复制代码
//基础的组件接口
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个或者多个装饰器子类,在各个装饰器子类中实现了需要对原有接口进行动态添加行为的具体实现。

具体装饰器实例

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

cpp 复制代码
#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;
}

代码运行解析

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

  • 初始状态及加牛奶
cpp 复制代码
// 初始创建
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对象
*/
  • 加糖过程
cpp 复制代码
// 加糖
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()
*/
  • 加巧克力过程
cpp 复制代码
// 加巧克力
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()
*/
  • 资源释放过程
cpp 复制代码
// 当coffee离开作用域时,析构顺序:
1. 首先析构ChocolateDecorator
2. 然后析构SugarDecorator
3. 接着析构MilkDecorator
4. 最后析构SimpleCoffee

/* 析构过程是一个递归的过程:
1. ~ChocolateDecorator() 
   -> 析构其coffee_成员(SugarDecorator)
      -> ~SugarDecorator()
         -> 析构其coffee_成员(MilkDecorator)
            -> ~MilkDecorator()
               -> 析构其coffee_成员(SimpleCoffee)
                  -> ~SimpleCoffee()
*/
相关推荐
CodeClimb1 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
等一场春雨1 小时前
Java设计模式 九 桥接模式 (Bridge Pattern)
java·设计模式·桥接模式
apz_end2 小时前
埃氏算法C++实现: 快速输出质数( 素数 )
开发语言·c++·算法·埃氏算法
仟濹2 小时前
【贪心算法】洛谷P1106 - 删数问题
c语言·c++·算法·贪心算法
北顾南栀倾寒3 小时前
[Qt]系统相关-网络编程-TCP、UDP、HTTP协议
开发语言·网络·c++·qt·tcp/ip·http·udp
old_power4 小时前
【PCL】Segmentation 模块—— 基于图割算法的点云分割(Min-Cut Based Segmentation)
c++·算法·计算机视觉·3d
涛ing4 小时前
21. C语言 `typedef`:类型重命名
linux·c语言·开发语言·c++·vscode·算法·visual studio
等一场春雨5 小时前
Java设计模式 十四 行为型模式 (Behavioral Patterns)
java·开发语言·设计模式
PaLu-LI5 小时前
ORB-SLAM2源码学习:Initializer.cc⑧: Initializer::CheckRT检验三角化结果
c++·人工智能·opencv·学习·ubuntu·计算机视觉
攻城狮7号7 小时前
【10.2】队列-设计循环队列
数据结构·c++·算法