装饰器设计模式学习

来自chatgpt

1.介绍

装饰器设计模式(Decorator Pattern)是一种结构型设计模式,允许你通过将对象包装 在一个装饰器类中来动态地为对象添加新的功能。装饰器模式通常用于当需要扩展一个类的功能时,而不需要修改该类的源代码。它通过创建一个装饰器类 来实现这一目标,并且可以将多个装饰器链式组合

装饰器模式的关键组成部分:

  1. Component(组件): 一个接口或抽象类,定义了被装饰对象的标准行为。
  2. ConcreteComponent(具体组件): 实现了Component接口的具体类,表示被装饰的对象。
  3. Decorator (装饰器): 一个实现了Component接口的抽象类,它持有一个Component对象并委托所有行为给它
  4. ConcreteDecorator(具体装饰器): 装饰器类的具体实现,它在调用委托的行为之前或之后执行额外的功能。

例子:

cpp 复制代码
#include <iostream>
#include <string>

// Component接口
class Car {
public:
    virtual ~Car() = default;
    virtual std::string getDescription() const = 0;
    virtual double cost() const = 0;
};

// ConcreteComponent:基础的Car类
class BasicCar : public Car {
public:
    std::string getDescription() const override {
        return "Basic Car";
    }

    double cost() const override {
        return 20000.0;
    }
    ~BasicCar(){
        std::cout << "Deleting BasicCar" << std::endl;
    }
};

// Decorator基类,需要继承Car类
class CarDecorator : public Car {
protected:
    Car* car;
public:
    CarDecorator(Car* c) : car(c) {}
    virtual ~CarDecorator() { 
        std::cout << "Deleting CarDecorator" << std::endl;
        delete car; 
    }
};

// ConcreteDecorator:添加空调功能
class WithAirConditioning : public CarDecorator {
public:
    WithAirConditioning(Car* c) : CarDecorator(c) {}

    std::string getDescription() const override {
        return car->getDescription() + " with Air Conditioning";
    }

    double cost() const override {
        return car->cost() + 1500.0;
    }
    ~WithAirConditioning() {
        std::cout << "Deleting WithAirConditioning" << std::endl;
    }
};

// ConcreteDecorator:添加天窗功能
class WithSunroof : public CarDecorator {
public:
    WithSunroof(Car* c) : CarDecorator(c) {}

    std::string getDescription() const override {
        return car->getDescription() + " with Sunroof";
    }

    double cost() const override {
        return car->cost() + 1200.0;
    }
    ~WithSunroof() {
        std::cout << "Deleting WithSunroof" << std::endl;
    }
};

int main() {
    Car* myCar = new BasicCar();
    std::cout << myCar->getDescription() << " costs $" << myCar->cost() << std::endl;

    myCar = new WithAirConditioning(myCar);
    std::cout << myCar->getDescription() << " costs $" << myCar->cost() << std::endl;

    myCar = new WithSunroof(myCar);
    std::cout << myCar->getDescription() << " costs $" << myCar->cost() << std::endl;

    delete myCar; // 删除装饰器时会自动删除基本车对象
    return 0;
}

// 运行结果
Basic Car costs $20000
Basic Car with Air Conditioning costs $21500
Basic Car with Air Conditioning with Sunroof costs $22700
Deleting WithSunroof
Deleting CarDecorator
Deleting WithAirConditioning
Deleting CarDecorator
Deleting BasicCar

2.装饰器基类可以不继承组件基类吗

在装饰器模式中,CarDecorator 必须实现 Car 接口 (或继承 Car 基类),否则无法保证装饰器与被装饰对象的行为一致性。这是一种模式的核心要求:装饰器与被装饰对象应该共享相同的接口

具体原因如下:

  1. 实现统一的接口

    通过实现 Car 接口,装饰器可以被视为一种特殊类型的 Car,从而在程序中保持一致的处理方式。客户端代码不需要知道具体是 Car 还是装饰器,依然可以调用 getDescription()cost() 等方法。

  2. 支持递归包装

    如果装饰器不实现 Car 接口,无法递归地嵌套装饰器。每个装饰器必须具有相同的接口,才能被进一步装饰。

如果不继承的话,就不支持装饰器嵌套递归。

cpp 复制代码
#include <iostream>
#include <string>

class Car {
public:
    virtual ~Car() = default;
    virtual std::string getDescription() const = 0;
    virtual double cost() const = 0;
};

class BasicCar : public Car {
public:
    std::string getDescription() const override {
        return "Basic Car";
    }

    double cost() const override {
        return 20000.0;
    }
};

// Decorator没有继承Car
class WithAirConditioning {
private:
    Car* car;
public:
    WithAirConditioning(Car* c) : car(c) {}

    std::string getDescription() const {
        return car->getDescription() + " with Air Conditioning";
    }

    double cost() const {
        return car->cost() + 1500.0;
    }
};

int main() {
    Car* myCar = new BasicCar();
    std::cout << myCar->getDescription() << " costs $" << myCar->cost() << std::endl;

    WithAirConditioning acDecorator(myCar);
    std::cout << acDecorator.getDescription() << " costs $" << acDecorator.cost() << std::endl;

    // 想进一步装饰为"加装天窗"的车,但WithAirConditioning无法作为Car传入:
    // WithSunroof sunroofDecorator(&acDecorator); // 错误,因为acDecorator不是Car类型

    delete myCar;
    return 0;
}

上述代码有两个问题:

  1. 无法递归嵌套装饰器WithAirConditioning 不是 Car 类型,不能进一步用其他装饰器包装。
  2. 客户端代码复杂 :需要额外处理 WithAirConditioning 的接口,且不能直接将它作为一个通用的 Car 来操作。

3.对象都是如何释放的?

CarDecorator中删除了被包装的对象car。

因此,当我们删除最外层装饰器(WithSunroof)时:

  1. WithSunroof 的析构函数会调用其基类 CarDecorator 的析构函数。
  2. CarDecorator 的析构函数会 delete 它所包装的 WithAirConditioning 对象。
  3. 类似地,WithAirConditioning 的析构函数会继续调用 CarDecorator 的析构函数,最终删除 BasicCar 对象。

这种递归删除确保了所有层级的对象都被正确释放。

4.使用智能指针的版本

// 没太看懂这个版本的。

cpp 复制代码
#include <iostream>
#include <memory>
#include <string>

class Car {
public:
    virtual ~Car() = default;
    virtual std::string getDescription() const = 0;
    virtual double cost() const = 0;
};

class BasicCar : public Car {
public:
    std::string getDescription() const override {
        return "Basic Car";
    }
    double cost() const override {
        return 20000.0;
    }
};

class CarDecorator : public Car {
protected:
    std::unique_ptr<Car> car;
public:
    CarDecorator(std::unique_ptr<Car> c) : car(std::move(c)) {}
    virtual ~CarDecorator() = default;
};

class WithAirConditioning : public CarDecorator {
public:
    WithAirConditioning(std::unique_ptr<Car> c) : CarDecorator(std::move(c)) {}
    std::string getDescription() const override {
        return car->getDescription() + " with Air Conditioning";
    }
    double cost() const override {
        return car->cost() + 1500.0;
    }
};

class WithSunroof : public CarDecorator {
public:
    WithSunroof(std::unique_ptr<Car> c) : CarDecorator(std::move(c)) {}
    std::string getDescription() const override {
        return car->getDescription() + " with Sunroof";
    }
    double cost() const override {
        return car->cost() + 1200.0;
    }
};

int main() {
    std::unique_ptr<Car> myCar = std::make_unique<BasicCar>();
    myCar = std::make_unique<WithAirConditioning>(std::move(myCar));
    myCar = std::make_unique<WithSunroof>(std::move(myCar));

    std::cout << myCar->getDescription() << " costs $" << myCar->cost() << std::endl;
    return 0;
}
相关推荐
AD钙奶-lalala30 分钟前
Mac OS上搭建 http server
java
知识分享小能手1 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
茯苓gao4 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾4 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
皮皮林5514 小时前
SpringBoot 全局/局部双模式 Gzip 压缩实战:14MB GeoJSON 秒变 3MB
java·spring boot
weixin_456904275 小时前
Spring Boot 用户管理系统
java·spring boot·后端
趁你还年轻_5 小时前
异步编程CompletionService
java
DKPT5 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa5 小时前
HTML和CSS学习
前端·css·学习·html
sibylyue5 小时前
Guava中常用的工具类
java·guava