【HeadFirst 设计模式】装饰者模式的C++实现

news2025/2/25 3:21:16

一、案例背景

Starbuzz是以扩张速度最快而闻名的咖啡连锁店。如果你在街角看到它的店,在对面街上肯定还会看到另一家。因为扩张速度实在太快了,他们准备更新订单系统,以合乎他们的饮料供应要求。他们原先的类设计是这样的……

在这里插入图片描述

购买咖啡时,可以要求在其中加入各种调料,例如:蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha,也就是巧克力风味)或覆盖奶泡。星巴克会根据所加入的调料收取不同的费用。所以订单系统必须考虑到这些调料部分。这是他们的第一个尝试……

在这里插入图片描述

很明显,Starbuzz为自己制造了一个维护噩梦:如果牛奶的价格上扬怎么办?新增一种焦糖调料风味时怎么办?

二、案例分析

看到这么多类时你肯定也会被震惊到……那么问题来了,如何进行改进呢?一个直截了当的解决方案是利用实例变量和继承,就可以追踪这些调料。比如我们在基类中加上实例变量,这些布尔值代表是否加上该调料(牛奶,豆浆,摩卡,奶泡……):

#include <iostream>
#include <string>
using namespace std;

class Beverage
{
private:
    string description {};
    bool   milk {};
    bool   soy {};
    bool   mocha {};
    bool   whip {};

public:
    const string getDiscription()
    {
        return description;
    };
    void setDescription(const string& description)
    {
        this->description = description + "(Add " + (milk ? "Milk " : "") + (soy ? "& Soy " : "") + (mocha ? "& Mocha " : "") + (whip ? "& Whip " : "") + ")";
    }

    virtual const float cost()
    {
        return (milk ? 1 : 0) + (soy ? 2 : 0) + (mocha ? 1 : 0) + (whip ? 1.5 : 0);
    }

    const bool hasMilk() const
    {
        return milk;
    };
    void setMilk(const bool value)
    {
        milk = value;
    };
    const bool hasSoy() const
    {
        return soy;
    };
    void setSoy(const bool value)
    {
        soy = value;
    };
    const bool hasMocha() const
    {
        return mocha;
    };
    void setMocha(const bool value)
    {
        mocha = value;
    };
    const bool hasWhip() const
    {
        return whip;
    };
    void setWhip(const bool value)
    {
        whip = value;
    };
};

class HouseBlend : public Beverage
{
public:
    HouseBlend()
    {
        setMilk(true);
        setSoy(true);
        setDescription("House Blend");
    }
    const float cost() override
    {
        return 5.0 + Beverage::cost();
    }
};

class DarkRoast : public Beverage
{
public:
    DarkRoast()
    {
        setMilk(true);
        setWhip(true);
        setDescription("DarkRoast");
    }
    const float cost() override
    {
        return 8.0 + Beverage::cost();
    }
};

class Decaf : public Beverage
{
public:
    Decaf()
    {
        setMilk(true);
        setWhip(true);
        setSoy(true);
        setDescription("Decaf");
    }
    const float cost() override
    {
        return 10.0 + Beverage::cost();
    }
};

int main()
{
    cout << "我点了一杯" + HouseBlend().getDiscription() << ",花了" << HouseBlend().cost() << "元"<<endl;
    cout << "我点了一杯" + DarkRoast().getDiscription() << ",花了" << DarkRoast().cost() << "元"<<endl;
    cout << "我点了一杯" + Decaf().getDiscription() << ",花了" << Decaf().cost() << "元"<<endl;

    return 0;
}

看起来似乎还行。但是如果将来由于原材料上涨某些调料需要上涨价钱怎么办?如果出现了新的调料呢?如果顾客想要双倍摩卡的咖啡呢?

这些变化都需要我们去直接变更源码。

开放关闭原则:类应该对扩展开放,对修改关闭。

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可以搭配新的行为。这样的设计具有弹性,可以应对改变,可以接收新的功能来应对改变的需求。

让我们来看看使用装饰者模式是怎么解决问题的:

在这里插入图片描述
在这里插入图片描述

三、代码分析

这里给出相关案例的C++代码实现:

#include <iostream>
#include <string>
using namespace std;

class Beverage
{
protected:
    string description = "unknown Beverage";

public:
    virtual const string getDescription() const
    {
        return description;
    }
    virtual const double cost() const = 0;
};

class CondimentDecorator : public Beverage
{
public:
    virtual const string getDescription() const = 0;
};

class HouseBlend : public Beverage
{
public:
    HouseBlend()
    {
        description = "HouseBlend";
    }

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

class DarkRoast : public Beverage
{
public:
    DarkRoast()
    {
        description = "DarkRoast";
    }

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

class Decaf : public Beverage
{
public:
    Decaf()
    {
        description = "Decaf";
    }

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

class Milk : public CondimentDecorator
{
public:
    Beverage* beverage {};
    Milk(Beverage* beverage)
    {
        this->beverage = beverage;
    }

    const string getDescription() const override
    {
        return beverage->getDescription() + " & Milk";
    }

    const double cost() const override
    {
        return beverage->cost() + 1.0;
    }
};

class Soy : public CondimentDecorator
{
public:
    Beverage* beverage {};
    Soy(Beverage* beverage)
    {
        this->beverage = beverage;
    }

    const string getDescription() const override
    {
        return beverage->getDescription() + " & Soy";
    }

    const double cost() const override
    {
        return beverage->cost() + 2.0;
    }
};

class Mocha : public CondimentDecorator
{
public:
    Beverage* beverage {};
    Mocha(Beverage* beverage)
    {
        this->beverage = beverage;
    }

    const string getDescription() const override
    {
        return beverage->getDescription() + " & Mocha";
    }

    const double cost() const override
    {
        return beverage->cost() + 2.0;
    }
};

class Whip : public CondimentDecorator
{
public:
    Beverage* beverage {};
    Whip(Beverage* beverage)
    {
        this->beverage = beverage;
    }

    const string getDescription() const override
    {
        return beverage->getDescription() + " & Whip";
    }

    const double cost() const override
    {
        return beverage->cost() + 2.0;
    }
};

int main()
{
    Beverage* houseblend        = new Milk(new Soy(new HouseBlend()));
    Beverage* darkRoast         = new Milk(new Soy(new Whip(new DarkRoast())));
    Beverage* decaf             = new Milk(new Whip(new Decaf()));
    // 双倍摩卡
    Beverage* doubleMochaCoffee = new Milk(new Soy(new Mocha(new Mocha(new HouseBlend()))));

    cout << "我点了一杯" + houseblend->getDescription() << ",花了" << houseblend->cost() << "元" << endl;
    cout << "我点了一杯" + darkRoast->getDescription() << ",花了" << darkRoast->cost() << "元" << endl;
    cout << "我点了一杯" + decaf->getDescription() << ",花了" << decaf->cost() << "元" << endl;
    cout << "我点了一杯" + doubleMochaCoffee->getDescription() << ",花了" << doubleMochaCoffee->cost() << "元" << endl;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2041199.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTTP/1.1

目录 一、比较HTTP/1.0的优点 二、请求报文 1.请求报文 &#xff08;1&#xff09;格式 2.get请求 &#xff08;1&#xff09;请求行 &#xff08;2&#xff09;请求头 &#xff08;3&#xff09;请求体 3.post请求 &#xff08;1&#xff09;请求行 &#xff08;2&…

LLM - 微调(Fine-Tuning) Llama3 以及合并微调模型 教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/141218047 在微调 Llama3 大模型时&#xff0c;需要注意一些事项&#xff1a; 合适的预训练模型&#xff1a;不同的预训练模型具有不同的特点和适…

Selenium WebDriver 的 Microsoft Edge 驱动程序【附代码】

1、从 Microsoft Edge 驱动程序的官方网站下载与浏览器版本相匹配的驱动程序。 https://msedgewebdriverstorage.z22.web.core.windows.net/https://msedgewebdriverstorage.z22.web.core.windows.net/ 2、指向驱动程序&#xff08;msedgedriver.exe&#xff09;的路径 serv…

Datawhale X魔搭AI夏令营 AIGC方向Task01学习笔记

目录 一、开通PAL-DSW适用 二、报名可图Kolors-LoRA风格故事挑战赛 三、创建PAL实例 四、跑baseline 五、结果上传魔塔 六、关闭PAL 小白&#xff0c;刚跑通Baseline&#xff0c;初步了解了竞赛。 Datawhale官方的速通教程链接&#xff1a;Task 1 从零入门AI生图原理&am…

利用Matlab实现【图论】中的图

目录 前言 一、图论是什么&#xff1f; 1.基本概念 2.表达形式 二、使用matlab作图 1.作无向图 2.作有向图 总结 前言 本文将讲解如何使用matlab中的函数实现最短路径算法&#xff0c;所提供代码仅供参考&#xff0c;严禁用于数模比赛中使用&#xff01; 一、图论是什么&#…

Linux:进程管理,任务管理,监控系统

1&#xff0c;任务管理 从前台丢到后台&#xff1a;【ctrl】 z&#xff0c;如果正在使用vim&#xff0c;突然想干其他事情&#xff0c;但又不想关闭vim&#xff0c;只需要暂时将vim丢到后台等待即可&#xff08;暂停&#xff09;。 查看后台任务状态&#xff1a;jobs&#xff0…

许多人在网络上“裸奔”,你信吗?

现在网络的热议话题之一&#xff0c;是近年来不仅网络诈骗、电信诈骗比比皆是&#xff0c;而更加让人恐怖的是仅只一个QQ号码就可以查出你所有的个人信息。 ​在时下&#xff0c;我国个人隐私泄漏的现象比比皆是&#xff0c;而且不只是仅几大社交平台才存在着严重的漏洞。早前…

WPF 资源、引用命名空间格式、FrameworkElement、Binding、数据绑定

资源 对象级别独立文件 静态资源使用(StaticResource)指的是在程序载入内存时对资源的一次性使用&#xff0c;之后就不再去访问这个资源了。 动态资源使用&#xff08;DynamicResource&#xff09;使用指的是在程序运行过程中仍然会去访问资源。 显然&#xff0c;如果你确定…

【专题】全球商用服务机器人市场研究(2023)报告合集PDF分享(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p37366 近年来&#xff0c;随着人工智能、物联网和自动化技术的不断进步&#xff0c;商用服务机器人行业迅速崛起&#xff0c;展现出广阔的发展前景。从最初的实验室研发到如今的规模化应用&#xff0c;商用服务机器人已逐渐成为各行…

【解析几何笔记】3.向量分解定理

3. 向量分解定理 3.1 线性组合 有 n n n个向量 α 1 , α 2 , . . . , α n \pmb{\alpha}_{1},\pmb{\alpha}_{2},...,\pmb{\alpha}_{n} α1​,α2​,...,αn​&#xff0c;对应有 n n n个实数 λ 1 , λ 2 , . . . , λ n \lambda_{1},\lambda_{2},...,\lambda_{n} λ1​,λ2…

Springboot项目基础开发模式+注解

文章目录 web项目开发涉及基础servlettomcatstrutsssm架构SpringBoot架构tomcathibernate Springboot注解AAspectAutowiredAllargConstructorAround BBeanBuilder CConfigurationConditionalOnMissingBeanComponentComponentScanConfigurationPropertiesCrossOrigin DDocumente…

C语言——构造类型

构造类型 数据类型分类 结构体 结构体的定义 定义&#xff1a;自定义数据类型的一种&#xff0c;关键字 struct &#xff0c;结构体类型的变量可以存储多个不同数据类型的数据。 定义格式&#xff1a; struct 结构体名 { 数据类型1 成员名称1; 数据类型2 成员名称2; … } 注…

Golang | Leetcode Golang题解之第335题路径交叉

题目&#xff1a; 题解&#xff1a; func isSelfCrossing(distance []int) bool {n : len(distance)// 处理第 1 种情况i : 0for i < n && (i < 2 || distance[i] > distance[i-2]) {i}if i n {return false}// 处理第 j 次移动的情况if i 3 && di…

Mac系统如何下载安装Photoshop软件mac的新版指南!

Photoshop&#xff08;简称PS&#xff09;是一款广受欢迎的图像处理工具&#xff0c;在设计、摄影、广告等领域广泛应用。对于Mac用户&#xff0c;安装Photoshop可能会涉及到一些特殊的步骤。本文将为你提供在Mac上安装Photoshop的详细教程及常见问题的解决方案。 一、准备工作…

由微软开源GraphRAG加持的AI智能体,让律师变得更强大!

随着人工智能大模型时代的到来&#xff0c;LLM大语言模型、RAG增强检索、Graph知识图谱、Prompt提示词工程等技术的发展日新月异&#xff0c;也让各行各业更加期待技术带来的产业变革。 比如&#xff0c;法律行业&#xff0c;虽然通用大模型已经能够适配一些法律场景&#xff…

从自动化到智能化:物联网技术在转转智能质检中心的应用

1 背景2 物联网介绍 2.1 开篇故事2.2 物联网是什么2.3 物联网的基本组成 3 物联网技术选型和落地方案 3.1 应用层协议选型3.2 Broker 选型3.3 QoS 消息质量选型3.4 Broker 的部署方案 4 结语5 参考链接 1. 背景 在转转智能质检中心&#xff0c;随着业务的不断发展&#xff0c…

MQ死信对列

面试题&#xff1a;你们是如何保证消息不丢失的&#xff1f; 1、什么是死信 死信就是消息在特定场景下的一种表现形式&#xff0c;这些场景包括&#xff1a; 1. 消息被拒绝访问&#xff0c;即消费者返回 basicNack 的信号时 或者拒绝basicReject 2. 消费者发生异常&#xff0…

vue3在高德地图中制作气象扇形雷达图

这是效果图&#xff0c;每隔22.5就会有一个扇形区域&#xff0c;有三层区域&#xff0c;第一层是距离圆点5km&#xff0c;第二层是10km&#xff0c;第三层是50km。 第一步&#xff1a;高德地图中绘画圆 // 构造矢量圆形let circle new AMap.Circle({center: position.value, …

rv1126-rv1109-读取mipi摄像头ID

1.有那个一个需求,需要读取mipi摄像头ID 起初,我以为很简单,实际非常复杂; 2.移植原有的逻辑,就是操作I2C函数读取ID寄存器,但是一直失败; 3.然后发现是电源没有打开;然后电源又是在DTS里面配置的 4.配置电源后发现要配置时钟 5.配置时钟后发现还要配置摄像头的上…

Vue框架学习笔记-7

Vue-cli项目中的mixin Vue-cli 项目中的 mixin 是一种强大的功能&#xff0c;允许你在多个组件之间共享可复用的方法和/或选项。Mixin 本质上是一个对象&#xff0c;它可以包含组件选项中的任意选项&#xff0c;如数据、方法、生命周期钩子等。当组件使用 mixin 时&#xff0c…