首页 > 其他分享 >设计模式之装饰模式(学习笔记)

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

时间:2024-07-15 21:20:44浏览次数:12  
标签:std 器类 coffee 对象 getDescription 笔记 设计模式 装饰

定义

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

为什么使用装饰模式?

  1. 灵活性和可扩展性

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

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

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

装饰模式的实现步骤

  1. 抽象组件类

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

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

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

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

优缺点和适用场景

优点

  1. 灵活性

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

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

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

缺点

  1. 复杂性增加

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

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

适用场景

  1. 动态添加功能

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

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

咖啡店的例子

假设我们有一个咖啡店,卖基础的咖啡和各种附加调料(如牛奶、糖、巧克力等)。我们可以使用装饰模式来实现这种功能扩展。
#include <iostream>
#include <memory>  // 提供智能指针的定义和实现
// 抽象组件类:咖啡
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> coffee;
public:
    // 构造函数,接受一个被装饰对象的指针
    CoffeeDecorator(std::shared_ptr<Coffee> 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> 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> 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<Coffee> basicCoffee = std::make_shared<BasicCoffee>();
    std::cout << "Description: " << basicCoffee->getDescription() << ", Cost: " <<  basicCoffee->cost() << " RMB" << std::endl;
    // 用牛奶装饰基本咖啡
    std::shared_ptr<Coffee> coffeeWithMilk =  std::make_shared<MilkDecorator>(basicCoffee);
    std::cout << "Description: " << coffeeWithMilk->getDescription() << ", Cost: "  << coffeeWithMilk->cost() << " RMB" << std::endl;
    // 再用糖装饰已加牛奶的咖啡
    std::shared_ptr<Coffee> coffeeWithMilkAndSugar =  std::make_shared<SugarDecorator>(coffeeWithMilk);
    std::cout << "Description: " << coffeeWithMilkAndSugar->getDescription() << ",  Cost: " << coffeeWithMilkAndSugar->cost() << " RMB" << std::endl;
    return 0;
}

 

标签:std,器类,coffee,对象,getDescription,笔记,设计模式,装饰
From: https://www.cnblogs.com/best-doraemon/p/18303979

相关文章

  • 矩阵优化 DP 以及全局平衡二叉树实现的动态 DP 学习笔记
    矩阵乘法求斐波那契数列的第\(n\)项,其中\(n\le10^{18}\),对数\(m\)取模。写出转移方程,\(f_i=f_{i-1}+f_{i-2}\),直接算是\(O(n)\)的,似乎没什么办法优化。定义大小分别为\(n\timesp\)和\(p\timesm\)的矩阵(分别为\(a\)和\(b\))相乘的结果(矩阵\(c\))是一个大小为\(......
  • IP协议学习笔记
    目录IP地址格式IP分类CIDR和子网掩码介绍NAT+公网、私网地址CIDR与VLSMVLSM子网划分案例练习ReferenceIP的作用类似物理世界中的地址,用于定位机器的位置。只不过物理的地址是文字描述,计算机世界的IP是一串二进制数,并且它是有一定约定和规则的。下面我来学习关于IP的一些历......
  • 论文阅读笔记-LSM-bush
    首先介绍前置工作。缩写缩写全称FPRfalsepositiverate符号符号含义单位NtotaldatasizeblocksFbuffersizeblocksLnumberoflevelsM所有bloomfilter的平均bitsperbitbitspsumofFPRsacrossallBloomfilters$p_i$......
  • Docker学习笔记
    安装不要安装debian自带的docker:sudoaptinstalldocker-compose。debian11和debian12安装的都是v1,没有dockercompose命令。用官网的安装方式:https://docs.docker.com/engine/install/debian/#install-using-the-repository创建imagehttps://docs.docker.com/reference/cl......
  • 【笔记】Nmap工具原理探索
    【笔记】Nmap工具原理探索原文章:【THM】Nmap(Nmap工具使用简介)-学习-Hekeatsll-博客园(cnblogs.com)Nmap是一款跨平台的开源端口扫描软件,它用来扫描计算机的开放端口,以确定运行的网络服务,并推断出计算机运行的操作系统Nmap三种基本扫描类型TCP连接扫描(-sT)工作原理:通过......
  • 数据结构学习笔记——线性表
    链表1.单链表链表的插入:    (1)需要知道插入位置的前驱结点(从表头顺序遍历)    (2)先修改要插入的结点(新结点)的指针    (3)再修改前驱结点的指针链表的删除:    (1)要知道删除结点的前驱结点(从表头顺序遍历)    (2)只需要修改前驱结点的指......
  • 设计模式:使用闭包实现数据集加载过程的依赖倒转
    重点数据集和数据集使用到的参数可以保持不同,将这些不同放到配置文件中进行处理而不是修改获取数据集的加载代码,优点是:减少修改代码的出错统一数据加载的接口格式设计模式:使用闭包实现数据集加载过程的依赖倒转重点前言一、什么是装饰器1、装饰器的定义2、装饰器的使......
  • 支配树学习笔记
    先抛出一个问题:给一个有向图,问从\(1\)节点出发,求每个节点的受支配集。这里,支配的定义为:若从\(1\)结点出发到\(v\)节点的所有路径中,都必须经过\(u\)节点,则称\(u\)支配\(v\)。那么受支配集意思就是对于\(v\)点满足条件的\(u\)点的集合。那么根据支配的定义,我们可以......
  • 【文化课学习笔记】【物理】动量
    【物理】动量冲量基础概念定义力在时间上的积累。冲量一般用字母\(I\)表示。公式冲量\(I\)的表达式如下:\[I=F\cdott\]由于\(F\)的单位是\(\puN\),\(t\)的单位是\(s\),所以冲量的单位是\(\pu{N*s}\)。根据表达式可知,冲量是矢量,方向与\(F\)相同。注意:冲......
  • UE4材质笔记
    常用节点:添加贴图:TextureSample(贴图采样)常量数字:Constant(数字)Vector或长按数字并点击左键乘法:Multiply或长按M并点击左键常用插值:Lerp或长按L并点击左键加法:Add或者长按A键并点击左键减法:Subtract除法:Divide或者长按D键并点击左键1-x:搜索1-或者oneminus或者长按O键并点击左键绝......