• 设计模式之装饰模式(学习笔记)


    定义

    装饰模式(Decorator Pattern),又称为包装模式,是一种结构型设计模式。它允许在不改变现有对象结构的情况下,动态地添加新的功能。通过将每个功能封装在单独的装饰器类中,并且这些装饰器类通过引用原始对象来实现功能的组合,从而提供了灵活性和可扩展性的优势。装饰模式避免了通过继承方式增加功能所带来的复杂性,使得功能的添加和组合更加高效和清晰。

    为什么使用装饰模式?

    1. 灵活性和可扩展性

      • 装饰模式允许按需动态地添加或移除对象的功能,而不会影响其他部分的代码。
      • 通过组合不同的装饰器类,可以创建出多种不同的对象组合,以满足不同的需求,避免了静态继承所带来的类爆炸问题。
    2. 单一职责原则

      • 每个装饰器类只关注于一个特定的功能或责任,这符合单一职责原则,使得代码结构更加清晰和易于维护。
    3. 透明性

      • 装饰器类与原始对象实现相同的接口,因此对客户端来说是透明的。
      • 客户端可以像使用原始对象一样使用装饰后的对象,无需关心对象内部的具体装饰结构。

    装饰模式的实现步骤

    1. 抽象组件类

      • 定义了被装饰对象的接口,它可能是一个抽象类或接口,包含了所有具体组件类和装饰器类都会实现的方法(抽象被装饰者的行为)。
    2. 具体组件类

      • 实现抽象组件接口,表示原始对象的基本行为或功能(继承抽象组件类,被装饰者行为的具体实现)。
    3. 抽象装饰器类

      • 扩展了抽象组件类,同时持有一个指向抽象组件对象的引用。
      • 这个类可以选择性地添加一些额外的行为,但其主要作用是通过引用调用原始对象的方法(继承抽象组件类)。
    4. 具体装饰器类

      • 扩展了抽象装饰器类,通过在调用父类方法前后添加新的行为来实现功能的扩展。
      • 具体装饰器类可以是多个,可以互相组合,以形成复杂的装饰结构(继承抽象装饰器类)。

    优缺点和适用场景

    优点

    1. 灵活性

      • 动态地为对象添加功能,避免了静态继承的限制。
    2. 可扩展性

      • 通过组合不同的装饰器类,可以实现多种功能组合,符合开闭原则。
    3. 单一职责原则

      • 每个装饰器类只关注于一个功能,使得代码结构清晰。

    缺点

    1. 复杂性增加

      • 可能会导致装饰器类的数量增加,增加了系统的复杂度和理解难度。
    2. 装饰顺序问题

      • 如果装饰器的顺序不正确,可能会影响最终的功能实现。

    适用场景

    1. 动态添加功能

      • 当需要动态地为对象添加额外功能时,而又不希望生成大量子类时,可以使用装饰模式。
    2. 透明且灵活地扩展对象的功能

      • 当需要透明且灵活地扩展对象的功能时,装饰模式尤为适用,例如在不影响其他对象的情况下动态添加功能。

    咖啡店的例子

    假设我们有一个咖啡店,卖基础的咖啡和各种附加调料(如牛奶、糖、巧克力等)。我们可以使用装饰模式来实现这种功能扩展。
    复制代码
    #include 
    #include   // 提供智能指针的定义和实现
    // 抽象组件类:咖啡
    class Coffee {
    public:
        virtual ~Coffee() {}
        // 获取咖啡描述的方法,纯虚函数
        virtual std::string getDescription() const = 0;
        // 获取咖啡价格的方法,纯虚函数
        virtual double cost() const = 0;
    };
    // 具体组件类:基本咖啡
    class BasicCoffee : public Coffee {
    public:
        // 实现获取描述的方法
        std::string getDescription() const override {
            return "Basic Coffee";
        }
        // 实现获取价格的方法
        double cost() const override {
            return 5.0; // 基本咖啡的价格
        }
    };
    // 抽象装饰器类:咖啡装饰器
    class CoffeeDecorator : public Coffee {
    protected:
        // 持有一个指向被装饰对象的指针
        std::shared_ptr coffee;
    public:
        // 构造函数,接受一个被装饰对象的指针
        CoffeeDecorator(std::shared_ptr coffee) : coffee(coffee) {}
        // 实现获取描述的方法,调用被装饰对象的方法
        std::string getDescription() const override {
            return coffee->getDescription();
        }
        // 实现获取价格的方法,调用被装饰对象的方法
        double cost() const override {
            return coffee->cost();
        }
    };
    // 具体装饰器类:牛奶装饰器
    class MilkDecorator : public CoffeeDecorator {
    public:
        // 构造函数,接受一个被装饰对象的指针
        MilkDecorator(std::shared_ptr coffee) : CoffeeDecorator(coffee) {}
        // 实现获取描述的方法,添加牛奶的描述
        std::string getDescription() const override {
            return coffee->getDescription() + ", Milk";
        }
        // 实现获取价格的方法,添加牛奶的价格
        double cost() const override {
            return coffee->cost() + 1.5; // 牛奶的价格
        }
    };
    // 具体装饰器类:糖装饰器
    class SugarDecorator : public CoffeeDecorator {
    public:
        // 构造函数,接受一个被装饰对象的指针
        SugarDecorator(std::shared_ptr coffee) : CoffeeDecorator(coffee) {}
        // 实现获取描述的方法,添加糖的描述
        std::string getDescription() const override {
            return coffee->getDescription() + ", Sugar";
        }
        // 实现获取价格的方法,添加糖的价格
        double cost() const override {
            return coffee->cost() + 0.5; // 糖的价格
        }
    };
    int main() {
        // 创建一个基本咖啡对象
        std::shared_ptr basicCoffee = std::make_shared();
        std::cout << "Description: " << basicCoffee->getDescription() << ", Cost: " <<  basicCoffee->cost() << " RMB" << std::endl;
        // 用牛奶装饰基本咖啡
        std::shared_ptr coffeeWithMilk =  std::make_shared(basicCoffee);
        std::cout << "Description: " << coffeeWithMilk->getDescription() << ", Cost: "  << coffeeWithMilk->cost() << " RMB" << std::endl;
        // 再用糖装饰已加牛奶的咖啡
        std::shared_ptr coffeeWithMilkAndSugar =  std::make_shared(coffeeWithMilk);
        std::cout << "Description: " << coffeeWithMilkAndSugar->getDescription() << ",  Cost: " << coffeeWithMilkAndSugar->cost() << " RMB" << std::endl;
        return 0;
    }
    复制代码

     

  • 相关阅读:
    ubuntu20.04如何安装搜狗输入法
    FL Studio21.2.0官方中文版重磅发布
    【sfu】network线程和主线程
    小白必看:测试人有必要参考的软件测试工作规范
    E. Yet Another Array Counting Problem
    【Java编程】14_java8新特性之Steam
    如何使用uview中的loadmore上拉加载
    Windows原理深入学习系列-访问控制列表
    大数据知识扫盲
    Linux 救援模式
  • 原文地址:https://www.cnblogs.com/best-doraemon/p/18303979