装饰器(Decorator)

参考:

[装饰器模式 (refactoringguru.cn)](https://refactoringguru.cn/design-patterns/bridge)

装饰器模式 (biancheng.net)

design-patterns-cpp/decorator at master · JakubVojvoda/design-patterns-cpp · GitHub

一、什么是装饰器模式?

定义:在不改变已有对象的属性和方法的前提下,对已有对象的属性和功能进行拓展。

扩展一个对象,最常用的方法是在它所属类中添加成员或者方法,或者使用继承,创建新类。但是这些方法都是静态的,当你需要扩展的功能是可以选择的时候,你需要使用动态的方式给用户提供选择。

比如,你已经有一扇窗了,但你想选择是否需要增加纱窗,在已有的这扇窗上扩展,而不是其它的窗户。最典型的例子是煎饼果子。每个客户都有一个原始的煎饼果子,可以根据喜好来添加辅料。可以加火腿,加完火腿后还可以加鸡蛋,继续加培根等等。这就像俄罗斯套娃,在一个原始对象上不断增加功能,而不改变它。

在这里插入图片描述

二、实现

装饰器(Decorator)模式包含以下主要角色:

  1. 抽象构件(Component):定义一个接口,确定原始组件的基本功能。
  2. 具体构件(ConcreteComponent):实现接口,它就是被装饰者。
  3. 抽象装饰(Decorator):继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. 具体装饰(ConcreteDecorator):实现抽象装饰的相关方法,并给具体构件对象添加功能。

在这里插入图片描述

1、Decorator.cpp

/*
 * C++ Design Patterns: Decorator
 * Author: Jakub Vojvoda [github.com/JakubVojvoda]
 * 2016
 *
 * Source code is licensed under MIT License
 * (for more details see LICENSE)
 *
 */

#include <iostream>

/*
 * Component
 * 实现接口,它就是被装饰者。
 */
class Component
{
public:
  virtual ~Component() {}
  
  virtual void operation() = 0;
  // ...
};

/*
 * Concrete Component
 * 定义一个接口,确定原始组件的基本功能。
 */
class ConcreteComponent : public Component
{
public:
  ~ConcreteComponent() {}
  
  void operation()
  {
    std::cout << "Concrete Component operation" << std::endl;
  }
  // ...
};

/*
 * Decorator
 * 继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
 */
class Decorator : public Component
{
public:
  ~Decorator() {}
  
  Decorator( Component *c ) : component( c ) {}
  
  virtual void operation()
  {
    component->operation();
  }
  // ...

private:
  Component *component;
};

/*
 * Concrete Decorators
 * 具体的装饰者,给具体构件对象添加功能。
 */
class ConcreteDecoratorA : public Decorator
{
public:
  ConcreteDecoratorA( Component *c ) : Decorator( c ) {}
  
  void operation()
  {
    Decorator::operation();
    std::cout << "Decorator A" << std::endl;
  }
  // ...
};

class ConcreteDecoratorB : public Decorator
{
public:
  ConcreteDecoratorB( Component *c ) : Decorator( c ) {}
  
  void operation()
  {
    Decorator::operation();
    std::cout << "Decorator B" << std::endl;
  }
  // ...
};


int main()
{
  ConcreteComponent  *cc = new ConcreteComponent();
  ConcreteDecoratorB *db = new ConcreteDecoratorB( cc );
  ConcreteDecoratorA *da = new ConcreteDecoratorA( db );
  
  Component *component = da;
  component->operation();
  
  delete da;
  delete db;
  delete cc;
  
  return 0;
}

装饰器模式所包含的 4 个角色不是任何时候都要存在的,在有些应用环境下模式是可以简化的,如以下两种情况。

(1) 如果只有一个具体构件而没有抽象构件时,可以让抽象装饰继承具体构件,其结构图如图 4 所示。

在这里插入图片描述

(2) 如果只有一个具体装饰时,可以将抽象装饰和具体装饰合并,其结构图如图 5 所示。

在这里插入图片描述

2、煎饼果子

#include <iostream>
#include <string>

class Component
{
public:
  virtual ~Component() {}
  
  virtual std::string GetName() = 0;
  virtual double GetPrice() = 0;
};

// 煎饼
class PanCake : public Component
{
public:
  ~PanCake() {}
  
  std::string GetName() override {
    return "PanCake";
  }

  double GetPrice() override {
    return 5.0;
  }
};

class Decorator : public Component
{
public:
  ~Decorator() {}
  
  Decorator( Component *c ) : component( c ) {}
  
  std::string GetName() override {
    return component->GetName();
  }

  double GetPrice() override {
    return component->GetPrice();
  }

private:
  Component *component;
};

// 煎饼+鸡蛋
class PanCakeWithEgg : public Decorator
{
public:
  PanCakeWithEgg( Component *c ) : Decorator( c ) {}
  
  std::string GetName() override {
    return Decorator::GetName() + "+Egg";
  }

  double GetPrice() override {
    return Decorator::GetPrice() + 1.5;
  }
  // ...
};

// 煎饼+火腿
class PanCakeWithHam : public Decorator
{
public:
  PanCakeWithHam( Component *c ) : Decorator( c ) {}
  
  std::string GetName() override {
    return Decorator::GetName() + "+Ham";
  }

  double GetPrice() override {
    return Decorator::GetPrice() + 2.0;
  }
};


int main()
{
  PanCake *aa = new PanCake();
  PanCakeWithEgg *bb = new PanCakeWithEgg( aa );
  PanCakeWithHam *cc = new PanCakeWithHam( bb );

  std::cout << aa->GetName() << "  Price: " << aa->GetPrice() << std::endl;
  std::cout << bb->GetName() << "  Price: " << bb->GetPrice() << std::endl;
  std::cout << cc->GetName() << "  Price: " << cc->GetPrice() << std::endl;
  
  delete cc;
  delete bb;
  delete aa;
  
  return 0;
}

输出:

PanCake  Price: 5
PanCake+Egg  Price: 6.5
PanCake+Egg+Ham  Price: 8.5

三、优缺点,适用场景

优点

  • 遵守开闭原则,在不改变原有对象的情况下,动态的给一个对象扩展功能。
  • 通过选择的排列组合,可以实现不同效果。

缺点

  • 装饰器模式会增加许多子类,过度使用会增加程序得复杂性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值