【C++设计模式】详解装饰模式

2023年8月31日,周四上午

这是我目前碰到的最难的设计模式.....

非常难以理解而且比较灵活多半,学得贼难受,写得贼费劲.....

2023年8月31日,周四晚上19:48

终于写完了,花了一天的时间来学习装饰模式和写这篇博客。

虽然基本上把我今天的收获都写下来了,

但感觉写得还是不够好,有很多东西没有表达出来、表达清楚,

以后有空再更新吧....


目录


概述

装饰模式的含义

装饰模式是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。

使用装饰模式的好处

  • 可以动态地给对象添加责任,扩展对象功能。
  • 相比继承,装饰模式使用对象组合而不是继承来扩展功能。更加灵活。
  • 装饰模式支持开闭原则,扩展对象不修改其代码。

为什么需要装饰模式

  1. 动态扩展功能:装饰模式允许在运行时动态地给对象添加新的功能,而不需要修改原始对象的代码。这样可以避免类的继承层次的爆炸性增长,同时也更加灵活和可扩展。
  2. 单一职责原则:装饰模式通过将功能分散到多个装饰类中,每个装饰类只关注特定的功能,遵循了单一职责原则,使得代码更加清晰、可维护。
  3. 开放-封闭原则:装饰模式支持对现有代码的扩展,而不需要修改已有的代码,符合开放-封闭原则。这样可以避免因为修改现有代码而引入潜在的风险和错误。

什么时候使用装饰模式

  1. 需要在不改变现有对象结构的情况下,给对象增加新的功能或责任。
  2. 需要动态地给对象添加功能,并且这些功能可以组合和排列。
  3. 需要扩展一个类的功能,但是使用继承会导致类的继承层次过于庞大或不可行。
  4. 需要在运行时动态地给对象添加功能,而不是在编译时静态地确定功能。

情景

假设我有一个类Xxx,需要给它添加加法功能和减法功能,

但是由于这个类已经很复杂了、而且继承的层次已经很深了,

我不想改动里面的代码,也不想再多弄一个子类,那么怎么添加这两个功能呢?

cpp 复制代码
class Xxx:public Ppp{
public:
	void func1();
	void func2();
	void func3();
	void func4();
	void func5();
	void func6();
	void func7();
};

在这种情况下,可以使用装饰模式来添加这两个功能,

因为这样既不用改动类内部代码,也不用再多弄一个子类。

cpp 复制代码
// 原始类
class Xxx :public Ppp{
public:
  void func1() { /* 原始功能1的实现 */ }
  void func2() { /* 原始功能2的实现 */ }
  // ... 其他原始功能的实现
  void func7() { /* 原始功能7的实现 */ }
};

// 装饰类 - 加法功能
class AddDecorator : public Xxx {
private:
  Xxx* component;

public:
  AddDecorator(Xxx* component) : component(component) {}

  void func1() override {
    component->func1();
    // 加法功能的实现
  }
};

// 装饰类 - 减法功能
class SubtractDecorator : public Xxx {
private:
  Xxx* component;

public:
  SubtractDecorator(Xxx* component) : component(component) {}

  void func2() override {
    component->func2();
    // 减法功能的实现
  }
};

int main() {
  Xxx* xxx = new Xxx();

  // 使用加法功能的装饰类
  Xxx* xxxWithAddition = new AddDecorator(xxx);
  xxxWithAddition->func1();

  // 使用减法功能的装饰类
  Xxx* xxxWithSubtraction = new SubtractDecorator(xxx);
  xxxWithSubtraction->func2();

  delete xxxWithSubtraction;
  delete xxxWithAddition;
  delete xxx;

  return 0;
}

注意,这并不是一个完整的装饰模式,但有助于理解装饰模式的作用。

标准的装饰模式

装饰模式的主要特征

  • 有一个抽象构件类,定义对象的接口。
  • 具体构件类实现抽象构件类。
  • 有一个装饰类继承抽象构件类,包含抽象构件类的对象引用。
  • 装饰类可以调用父类接口实现扩展功能。

标准装饰模式程序示例

cpp 复制代码
#include <iostream>

// 抽象构件类
class Component {
public:
  virtual void func() = 0;
};

// 具体构件类
class ConcreteComponent : public Component {
public:
  void func() override {
    std::cout << "具体构件的操作" << std::endl;
  }
};

// 装饰类
class Decorator : public Component {
protected:
  Component* component;

public:
  Decorator(Component* component) : component(component) {}

  void func() override {
    if (component != nullptr) {
      component->func();
    }
  }
};

// 具体装饰类A
class ConcreteDecoratorA : public Decorator {
public:
  ConcreteDecoratorA(Component* component) : Decorator(component) {}

  void addedBehavior() {
    std::cout << "具体装饰类的附加行为A" << std::endl;
  }

  void func() override {
    Decorator::func();
    addedBehavior();
  }
};

// 具体装饰类B
class ConcreteDecoratorB : public Decorator {
public:
  ConcreteDecoratorB(Component* component) : Decorator(component) {}

  void addedBehavior() {
    std::cout << "具体装饰类的附加行为B" << std::endl;
  }

  void func() override {
    Decorator::func();
    addedBehavior();
  }
};

int main() {
  Component* component = new ConcreteComponent();
  
  //用decoratedComponentA修饰component
  Component* decoratedComponentA = new ConcreteDecoratorA(component);
  //用decoratedComponentB修饰被decoratedComponentA修饰过的component
  Component* decoratedComponentB = new ConcreteDecoratorB(decoratedComponentA);
  
  decoratedComponentB->func();
  
  //要记得释放申请的堆内存
  delete decoratedComponentA;
  delete decoratedComponentB;
  delete component;

  return 0;
}

我对标准装饰模式的思考

装饰模式是怎么实现的不改动类原来的代码和结构就增加新的功能的?

装饰器是如何装饰具体构建类的?

他们有同一个抽象父类

这意味这什么?它们都会有抽象构件类的纯虚函数

装饰器需要引入抽象构件类

能引入抽象构件类,意味传入具体构件类对象后,只能调用其中抽象构件类有的方法

装饰器会重写父类的方法,并在重写的方法里面调用具体构件类的方法

具体装饰器有自己独特的成员和方法

具体装饰器会把具体构件类装入

具体装饰器会重写抽象装饰器的方法,并在重写的方法里面调用父类的方法和自己独特的方法

不管怎样,具体装饰类必须引入抽象构件类并传入具体构件类对象

装饰模式的核心在于重写?

重写的时候加入额外的方法

具体构件类需要实现抽象构件类的纯虚函数,否则无法被创建

装饰器的作用是什么?

引入具体构件类

在重写方法中调用具体构件类的方法

抽象构件类的作用是什么?

具体构件类的作用是什么?

定义最基础的功能

具体装饰器的作用是什么?

写额外的功能,然后通过重写方法加入额外的功能

使用标准装饰模式来写的示例程序

cpp 复制代码
#include<iostream>

class Product{
public:
	virtual void buy()=0;
};

class Computer:public Product{
public:
	void buy ()override{
		std::cout<<"买了一台电脑"<<std::endl;
	}
};

class Decorate:public Product{
private:
	Product *product;
public:
	Decorate(Product *product):product(product){};
	
	void buy() override{
		if(product!=nullptr){
			product->buy();
		}
	}
};

class Mouse:public Decorate{
public:
	Mouse(Product *product):Decorate(product){};
	
	void buy() override{
		Decorate::buy();
		std::cout<<"又买了鼠标"<<std::endl;
	}
};

class KeyBoard:public Decorate{
public:
	KeyBoard(Product *product):Decorate(product){};
	
	void buy() override{
		Decorate::buy();
		std::cout<<"又买了键盘"<<std::endl;
	}
};

int main(){
	Product *computer=new Computer();
	Mouse *mouse=new Mouse(computer);
	KeyBoard *keyBoard=new KeyBoard(mouse);
	keyBoard->buy();
}
相关推荐
lxyzcm7 小时前
深入理解C++23的Deducing this特性(上):基础概念与语法详解
开发语言·c++·spring boot·设计模式·c++23
越甲八千7 小时前
重温设计模式--单例模式
单例模式·设计模式
Vincent(朱志强)7 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
诸葛悠闲8 小时前
设计模式——桥接模式
设计模式·桥接模式
捕鲸叉13 小时前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
小小小妮子~13 小时前
框架专题:设计模式
设计模式·框架
先睡13 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
Damon_X21 小时前
桥接模式(Bridge Pattern)
设计模式·桥接模式
越甲八千1 天前
重温设计模式--享元模式
设计模式·享元模式
码农爱java1 天前
设计模式--抽象工厂模式【创建型模式】
java·设计模式·面试·抽象工厂模式·原理·23种设计模式·java 设计模式