C++ 【装饰模式】

简单介绍

装饰模式是一种结构型设计模式,当你需要在一个类里扩展一个或者额外添加一个新行为,可以用装饰模式在原有基础上加一层 " 装饰 "

开发时遇到的问题

例如你有一个开发一个提供通知功能的库, 其他程序可使用它向用户发送关于重要事件的通知
但是用户会在使用中希望添加许多功能。
(有的用户希望在微信上接收消息, 有的用户则希望在 QQ 上接收消息,有的希望两个都接收)
代码量膨胀
这时候如果盲目使用继承扩展通知类就会导致代码量膨胀。

继承也会引发一些问题:

  • 继承是静态的。 你无法在运行时更改已有对象的继承关系, 只能使用由不同子类创建的对象来替代当前的整个对象。
  • 子类只能有一个父类。 大部分编程语言不允许一个类同时继承多个类的行为。

有一种方法是使用聚合组合 , 两者的工作方式几乎一模一样: 一个对象包含指向另一个对象的引用, 并将部分工作委派给引用对象; 继承中的对象则继承了父类的行为, 它们自己能够完成这些工作。

这时候就可以引入装饰模式的思想:装饰器连接其他目标对象(上述的扩展的功能类),包含目标对象的方法(在微信上接收消息),会将所有接收到的请求委派给目标对象,并且可以在委派前后对请求进行处理。可能会改变最终结果。
在这里插入图片描述
客户端无法分辨是通知类Notifier 还是装饰器BaseDecorator 使用了通用的接口,减少耦合

UML图

在这里插入图片描述

实现方式

确保业务逻辑可用一个基本组件及多个额外可选层次表示。(咖啡为基本组件,选择添加的东西为可选层次)
以下代码主要注意继承关系以及 聚合关系

#include <iostream>
#include <bits/stdc++.h>

// 装饰模式

using namespace std;
// 当子类有相同的字段,需要向上提,(可以设置一个中间类)
class coffee
{
public:
    coffee() {}
    virtual string desc() = 0;
    virtual int money() = 0;
    ~coffee() {}
};

class BlockCoffee : public coffee
{
public:
    virtual string desc()
    {
        return "黑咖啡";
    }
    virtual int money()
    {
        return 10;
    }
};

class Decorator : public coffee
{
public:
    Decorator(coffee *coffee) : decor(coffee)
    {
    }
    coffee *decor;//聚合关系
};

class suger : public Decorator
{
public:
    suger(coffee *coffee) : Decorator(coffee) {} // 调用基类的构造器
    virtual string desc()
    {
        return decor->desc() + "一勺糖";
    }
    virtual int money()
    {
        return decor->money() + 1;
    }
};
class Chocolate : public Decorator
{
public:
    Chocolate(coffee *coffee) : Decorator(coffee) {}
    virtual string desc()
    {
        return decor->desc() + "一块巧克力";
    }
    virtual int money()
    {
        return decor->money() + 3; // 一块巧克力3¥
    }
};
int main()
{
    BlockCoffee *bc = new BlockCoffee();
    printf("一杯黑咖啡 %s %d",bc->desc(),bc->money());
    cout << bc->desc() << " " << bc->money() << endl;
    suger *su = new suger(bc);
    // 计算一杯黑咖啡加一勺糖的价格
    cout << su->desc() << " " << su->money() << endl;
    // 加一勺牛奶
    Chocolate *ct = new Chocolate(su);
    // 计算一杯黑咖啡加一勺糖、加一勺牛奶、加一块巧克力的价格
    cout << ct->desc() << " " << ct->money() << endl;
    system("pause");
}

适合应用场景

如果你希望在无需修改代码的情况下即可使用对象, 且希望在运行时为对象新增额外的行为, 可以使用装饰模式。

当需要在运行时为对象新增额外的行为时,可以通过创建不同的具体装饰者对象来实现。这些具体装饰者对象可以根据需要进行组合,从而实现各种不同的行为组合,而无需修改原始对象的代码。保持了接口不变

如果用继承来扩展对象行为的方案难以实现或者根本不可行, 你可以使用该模式。

许多编程语言使用 final最终关键字来限制对某个类的进一步扩展。 复用最终类已有行为的唯一方法是使用装饰模式: 用封装器对其进行封装。

与其他模式的关系
  • 适配器模式 可以对已有对象的接口进行修改, 装饰模式 则能在不改变对象接口的前提下强化对象功能。 此外, 装饰还支持递归组合(在装饰上装饰), 适配器则无法实现。

  • 适配器模式 能为被封装对象提供不同的接口, 代理模式能为对象提供相同的接口, 装饰模式 则能为对象提供加强的接口。

  • 责任链模式装饰模式的类结构非常相似。 两者都依赖递归组合将需要执行的操作传递给一系列对象。 但是, 两者有几点重要的不同之处。

责任链的管理者可以相互独立地执行一切操作, 还可以随时停止传递请求。 另一方面, 各种装饰可以在遵循基本接口的情况下扩展对象的行为。 此外, 装饰无法中断请求的传递。

  • 组合模式 和装饰的结构图很相似, 因为两者都依赖递归组合来组织无限数量的对象。

装饰类似于组合, 但其只有一个子组件。 此外还有一个明显不同: 装饰为被封装对象添加了额外的职责, 组合仅对其子节点的结果进行了 “求和”。
但是, 模式也可以相互合作: 你可以使用装饰来扩展组合树中特定对象的行为。

  • 大量使用组合模式 和装饰的设计通常可从对于原型模式的使用中获益。 你可以通过该模式来复制复杂结构, 而非从零开始重新构造。

  • 装饰可让你更改对象的外表, 策略模式则让你能够改变其本质。

  • 装饰代理有着相似的结构, 但是其意图却非常不同。 这两个模式的构建都基于组合原则, 也就是说一个对象应该将部分工作委派给另一个对象。
    不同之处在于 代理 通常自行管理其服务对象的生命周期, 而 装饰 的生成则总是由客户端进行控制

  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值