装饰器设计模式学习

来自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;
}
相关推荐
我叫啥都行几秒前
计算机基础复习12.22
java·jvm·redis·后端·mysql
寻找沙漠的人7 分钟前
JavaEE 导读与环境配置
java·spring boot·java-ee
taoyong00117 分钟前
Java线程核心01-中断线程的理论原理
java·开发语言
Yhame.43 分钟前
Java 集合框架中的 List、ArrayList 和 泛型 实例
java
coding侠客43 分钟前
Spring Boot 多数据源解决方案:dynamic-datasource-spring-boot-starter 的奥秘
java·spring boot·后端
委婉待续1 小时前
java抽奖系统(八)
java·开发语言·状态模式
weixin_537590451 小时前
《Java编程入门官方教程》第八章练习答案
java·开发语言·servlet
CodeClimb2 小时前
【华为OD-E卷-最左侧冗余覆盖子串 100分(python、java、c++、js、c)】
java·python·华为od
创意锦囊2 小时前
随时随地编码,高效算法学习工具—E时代IDE
ide·学习·算法