C++ 中的装饰器设计模式
装饰器 是一种 结构型设计模式,它允许我们在不改变或干扰现有对象结构的情况下为其添加新功能。它通过将该对象置于另一个包含新行为的特殊 包装对象 中,来为对象附加 新行为。
让我们通过一个 示例 来详细理解。想象你有一个简单的 文本编辑器 应用程序,具有基本的文本功能,如编写、保存或编辑文本。现在,你的经理要求你添加一些新功能,如 拼写检查 和 语法检查。如果你使用 Decorator Design Pattern,就可以轻松添加这些新功能,而无需更改文本编辑器的现有代码。你可以为拼写检查和语法检查创建独立的 decorator classes,这些类将围绕原始的文本编辑器类进行包装。
装饰器设计模式的组件
Decorator Design Pattern 有 四个主要组件,如下图所示。此外,client 是使用这些组件执行操作的一方。我们添加了两个
让我们详细了解这些组件 −
- Component − 这是一个 interface 或 abstract class,它为 concrete component 和 decorators 定义了共同的方法。在我们的示例中,这将是文本编辑器的接口。
- Concrete Component − 这是实现 component 接口的类,代表我们要为其添加新功能的 原始对象。在我们的示例中,这将是基本的文本编辑器类。
- Decorator − 这是一个 abstract class,它也实现 component 接口,并持有一个 component 对象的引用。decorator 类负责为 component 对象添加新功能。在我们的示例中,这将是文本编辑器的抽象 decorator 类。
- Concrete Decorators − 这些是扩展 decorator 类的类,并实现 新功能。在我们的示例中,这些将是 spell checker 和 grammar checker 类,它们为文本编辑器添加各自的功能。
C++ 中装饰器设计模式的实现
之前,我们讨论了Decorator Design Pattern的组成部分。现在,让我们看看如何在 C++ 中实现它。
在这个实现中,我们将创建一个简单的Music Player应用程序,其中有一个可以播放音乐的basic music player。然后,我们将创建decorators,为音乐播放器添加新功能,如Shuffle和Repeat。
实现装饰器设计模式的步骤
让我们看看如何在 C++ 中实现Decorator Design Pattern
- 为音乐播放器创建一个Component接口。
- 实现一个Concrete Component类,用于基本的音乐播放器。
- 创建一个抽象的Decorator类,实现Component接口。
- 实现两个具体的Decorator类,用于shuffle和repeat功能。
- 创建一个Client类来使用带有装饰器的音乐播放器。
装饰器设计模式的 C++ 代码
让我们看看音乐播放器应用的 Decorator Design Pattern 实现的 C++ 代码。
在这里,我们将使用之前讨论的所有Decorator Design Pattern组成部分。我们将为音乐播放器创建一个Component接口,一个用于基本音乐播放器的Concrete Component类,一个实现Component接口的抽象Decorator类,以及用于shuffle和repeat功能的具体的Decorator类。
#include <iostream>
using namespace std;
// Component(组件)
class MusicPlayer {
public:
virtual void play() = 0;
virtual ~MusicPlayer() {}
};
// Concrete Component(具体组件)
class BasicMusicPlayer : public MusicPlayer {
public:
void play() {
cout << "Playing music..." << endl;
}
};
// Decorator(装饰器)
class MusicPlayerDecorator : public MusicPlayer {
protected:
MusicPlayer* player;
public:
MusicPlayerDecorator(MusicPlayer* p) : player(p) {}
virtual void play() {
player->play();
}
virtual ~MusicPlayerDecorator() {
delete player;
}
};
// 用于 Shuffle 的具体装饰器
class ShuffleDecorator : public MusicPlayerDecorator {
public:
ShuffleDecorator(MusicPlayer* p) : MusicPlayerDecorator(p) {}
void play() {
cout << "Shuffling playlist..." << endl;
player->play();
}
};
// 用于 Repeat 的具体装饰器
class RepeatDecorator : public MusicPlayerDecorator {
public:
RepeatDecorator(MusicPlayer* p) : MusicPlayerDecorator(p) {}
void play() {
cout << "Repeating current song..." << endl;
player->play();
}
};
// Client(客户端)
int main() {
MusicPlayer* player = new BasicMusicPlayer();
MusicPlayer* shufflePlayer = new ShuffleDecorator(player);
MusicPlayer* repeatShufflePlayer = new RepeatDecorator(shufflePlayer);
cout << "Basic Music Player:" << endl;
player->play();
cout << "\nMusic Player with Shuffle:" << endl;
shufflePlayer->play();
cout << "\nMusic Player with Shuffle and Repeat:" << endl;
repeatShufflePlayer->play();
delete repeatShufflePlayer; // 这也将删除 shufflePlayer 和 player
return 0;
}
以下是上述代码的输出 −
Basic Music Player: Playing music... Music Player with Shuffle: Shuffling playlist... Playing music... Music Player with Shuffle and Repeat: Repeating current song... Shuffling playlist... Playing music...
Decorator 设计模式的优缺点
下表突出了使用 Decorator Design Pattern 的优缺点 −
| 优点 | 缺点 |
|---|---|
| 允许在不修改现有代码的情况下添加新功能。 | 可能导致大量小类。 |
| 通过使用组合而非继承来促进代码复用。 | 可能使代码变得更加复杂且难以理解。 |
| 通过将功能分解到单独的类中,遵循Single Responsibility Principle。 | 由于多层包装,调试可能更加困难。 |
| 通过动态添加职责来提升灵活性。 | 由于多层包装,可能存在性能开销。 |
| 可以与其他design patterns结合用于更复杂的场景。 | 可能导致对象identity和equality checks问题。 |
何时使用 Decorator 设计模式?
以下是一些您应该考虑使用 Decorator Design Pattern 的场景 −
- 动态地为对象添加更多功能。
- 当您希望避免由于多个功能组合导致的类爆炸时。
- 通过将功能分解到单独的类中,遵循Single Responsibility Principle。
- 为单个对象添加职责,而不影响同一类的其他对象。
- 在运行时增强对象的行为。
Decorator 设计模式的现实世界应用
下图展示了 Decorator 设计模式的一些现实世界应用 -
结论
在本章中,我们学习了 Decorator Design Pattern,其组件、实现步骤以及一个 C++ 代码示例。我们还讨论了使用此模式的优缺点、何时使用它,以及一些现实世界应用。Decorator Design Pattern 是一个强大的工具,它允许我们在不改变现有对象结构的情况下添加新功能,从而使我们更容易维护和扩展程序代码。