C++ 结构型设计模式有哪些?怎么用?

文章导读
Previous Quiz Next 结构型设计模式 是一种将不同的 class 和 object 组合或排列以形成更大、更复杂的结构,从而更好地解决特定问题的方法。这些模式通过识别实体之间关系的简单实现方式来简化设计。
📋 目录
  1. A 结构型设计模式的类型
  2. B C++ 中结构型设计模式示例
  3. C 何时使用结构型设计模式?
  4. D 结构型设计模式的优缺点
  5. E 结论
A A

C++ 中的结构型设计模式



Previous
Quiz
Next

结构型设计模式 是一种将不同的 class 和 object 组合或排列以形成更大、更复杂的结构,从而更好地解决特定问题的方法。这些模式通过识别实体之间关系的简单实现方式来简化设计。

以一座建筑物为例。建筑物由不同的部分组成,如墙壁屋顶窗户。每个部分都有其自身的特征和功能,但组合起来后,它们形成一个完整的结构,用于实现特定目的。同样,在软件设计中,结构型设计模式有助于将不同的 class 和 object 组合起来,创建出更容易理解和维护的更大结构。

结构型设计模式关注class 和 object 如何组合以形成更大结构。这些模式提供了一种创建对象和 class 之间关系的方式,这种方式易于理解和维护。

结构型设计模式的类型

结构型设计模式有7 种主要类型——

  • Adapter Pattern − 将一个 class 的接口转换为客户端期望的另一个接口。
  • Bridge Pattern − 将抽象与实现分离,使两者可以独立变化。
  • Composite Pattern − 将对象组合成树形结构以表示部分-整体层次结构。
  • Decorator Pattern − 在不改变对象结构的情况下动态地为其添加新功能。
  • Facade Pattern − 为复杂的子系统提供简化的接口。
  • Flyweight Pattern − 通过共享对象共同部分来减少内存占用。
  • Proxy Pattern − 为另一个对象提供代理或占位符以控制对其的访问。

在后续章节中,我们将详细介绍所有这些设计模式。

结构型设计模式的图示

以下是 C++ 中结构型设计模式的图示

Structural Design Patterns in C++

我们以一个商场为例。整个商场是Composite Design Pattern的示例,其入口展示了Facade Design Pattern的工作方式,入口处的保安Proxy Design Pattern的示例,商场的管理系统Bridge Design Pattern的示例,货币兑换点Adapter Design Pattern的示例,灯、横幅、灯光Decorator Design Pattern的示例,而长椅和灯Flyweight Design Pattern的示例,因为它们被多个区域共享,如美食广场、停车场等。

C++ 中结构型设计模式示例

在这个示例中,我们创建了一个媒体播放器应用程序,它使用多种结构型设计模式来提供灵活且易于维护的解决方案。Adapter Pattern 允许我们将不同媒体文件格式转换为媒体播放器能够理解的通用接口。

Composite Pattern 允许我们创建媒体文件的播放列表,这些文件可以一起播放。Decorator Pattern 允许我们动态地为媒体播放器添加新功能。Facade Pattern 为复杂的媒体播放器子系统提供简化的接口。Proxy Pattern 控制对媒体播放器的访问,并确保只有授权用户才能播放某些媒体文件。

Bridge Pattern 将媒体播放器的抽象与其实现分离,允许我们轻松地在不同媒体播放器实现之间切换。最后,Flyweight Pattern 通过共享媒体文件的公共部分来减少内存使用。

Example of Structural Design Patterns in C++

以下代码演示了在 C++ 中结合使用多种结构型设计模式来创建媒体播放器应用程序 —

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

// MediaFile 接口
class MediaFile {
   public:
      virtual string getType() = 0;
      virtual void play() = 0;
      virtual ~MediaFile() = default;
};

// 具体的 MediaFile 类
class MP3File : public MediaFile {
   public:
      string getType() {
         return "MP3";
      }
      void play() {
         cout << "Playing MP3 file" << endl;
      }
};

class MP4File : public MediaFile {
   public:
      string getType() {
         return "MP4";
      }
      void play() {
         cout << "Playing MP4 file" << endl;
      }
};

// Adapter Pattern
class MediaAdapter : public MediaFile {
   private:
      shared_ptr<MediaFile> mediaFile;
   public:
      MediaAdapter(shared_ptr<MediaFile> file) {
         mediaFile = file;
      }
      string getType() {
         return mediaFile->getType();
      }
      void play() {
         mediaFile->play();
      }
};

// Composite Pattern
class Playlist : public MediaFile {
   private:
      vector<shared_ptr<MediaFile>> mediaFiles;
   public:
      void add(shared_ptr<MediaFile> file) {
         mediaFiles.push_back(file);
      }
      string getType() {
         return "Playlist";
      }
      void play() {
         cout << "Playing playlist:" << endl;
         for (auto& file : mediaFiles) {
            file->play();
         }
      }
};

// Decorator Pattern
class MediaDecorator : public MediaFile {
   protected:
      shared_ptr<MediaFile> mediaFile;
   public:
      MediaDecorator(shared_ptr<MediaFile> file) {
         mediaFile = file;
      }
      string getType() {
         return mediaFile->getType();
      }
      void play() {
         mediaFile->play();
      }
};

class VisualEffectDecorator : public MediaDecorator {
   public:
      VisualEffectDecorator(shared_ptr<MediaFile> file) : MediaDecorator(file) {}
      void play() {
         mediaFile->play();
         cout << "Adding visual effects" << endl;
      }
};

class AudioEnhancementDecorator : public MediaDecorator {
   public:
      AudioEnhancementDecorator(shared_ptr<MediaFile> file) : MediaDecorator(file) {}
      void play() {
         mediaFile->play();
         cout << "Enhancing audio quality" << endl;
      }
};

// Facade Pattern
class MediaPlayer {
   private:
      shared_ptr<MediaFile> mediaFile;
   public:
      MediaPlayer(shared_ptr<MediaFile> file) {
         mediaFile = file;
      }
      void play() {
         mediaFile->play();
      }
};

// Proxy Pattern
class MediaProxy : public MediaFile {
   private:
      shared_ptr<MediaFile> mediaFile;
      bool authorized;
   public:
      MediaProxy(shared_ptr<MediaFile> file, bool auth) {
         mediaFile = file;
         authorized = auth;
      }
      string getType() {
         return mediaFile->getType();
      }
      void play() {
         if (authorized) {
            mediaFile->play();
         } else {
            cout << "Access denied" << endl;
         }
      }
};

// Bridge Pattern
class MediaImplementation {
   public:
      virtual void play() = 0;
      virtual ~MediaImplementation() = default;
};

class VLCImplementation : public MediaImplementation {
   public:
      void play() {
         cout << "Playing using VLC implementation" << endl;
      }
};

class WindowsMediaImplementation : public MediaImplementation {
   public:
      void play() {
         cout << "Playing using Windows Media implementation" << endl;
      }
};

class MediaAbstraction {
   protected:
      shared_ptr<MediaImplementation> implementation;
   public:
      MediaAbstraction(shared_ptr<MediaImplementation> impl) {
         implementation = impl;
      }
      virtual void play() = 0;
      virtual ~MediaAbstraction() = default;
};

class AdvancedMediaPlayer : public MediaAbstraction {
   public:
      AdvancedMediaPlayer(shared_ptr<MediaImplementation> impl) : MediaAbstraction(impl) {}
      void play() {
         implementation->play();
      }
};

// Flyweight Pattern
class MediaMetadata {
   private:
      string title;
      string artist;
      string album;
   public:
      MediaMetadata(string t, string ar, string al) : title(t), artist(ar), album(al) {}
      string getTitle() { return title; }
      string getArtist() { return artist; }
      string getAlbum() { return album; }
      void display() {
         cout << "Title: " << title << ", Artist: " << artist << ", Album: " << album << endl;
      }
};

class MediaMetadataFactory {
   private:
      vector<shared_ptr<MediaMetadata>> metadataPool;
   public:
      shared_ptr<MediaMetadata> getMetadata(string title, string artist, string album) {
         for (auto& meta : metadataPool) {
            if (meta->getTitle() == title &&
                meta->getArtist() == artist &&
                meta->getAlbum() == album) {
               return meta;
            }
         }
         shared_ptr<MediaMetadata> newMeta = make_shared<MediaMetadata>(title, artist, album);
         metadataPool.push_back(newMeta);
         return newMeta;
      }
};

int main() {
   // 创建媒体文件
   auto mp3 = make_shared<MP3File>();
   auto mp4 = make_shared<MP4File>();
   auto adaptedMp3 = make_shared<MediaAdapter>(mp3);
   auto adaptedMp4 = make_shared<MediaAdapter>(mp4);

   // 创建播放列表
   auto playlist = make_shared<Playlist>();
   playlist->add(adaptedMp3);
   playlist->add(adaptedMp4);
   playlist->add(make_shared<VisualEffectDecorator>(adaptedMp4));
   playlist->add(make_shared<AudioEnhancementDecorator>(adaptedMp3));

   // 创建媒体播放器外观
   auto player = make_shared<MediaPlayer>(playlist);
   player->play();

   // 创建媒体代理
   auto proxy1 = make_shared<MediaProxy>(adaptedMp4, false);
   proxy1->play();
   auto proxy2 = make_shared<MediaProxy>(adaptedMp4, true);
   proxy2->play();

   // 使用桥接模式创建高级媒体播放器
   auto vlcImpl = make_shared<VLCImplementation>();
   shared_ptr<MediaAbstraction> advancedPlayer = make_shared<AdvancedMediaPlayer>(vlcImpl);
   advancedPlayer->play();

   auto wmImpl = make_shared<WindowsMediaImplementation>();
   advancedPlayer = make_shared<AdvancedMediaPlayer>(wmImpl);
   advancedPlayer->play();

   // 使用享元模式创建媒体元数据
   auto metadataFactory = make_shared<MediaMetadataFactory>();
   auto meta1 = metadataFactory->getMetadata("Song1", "Artist1", "Album1");
   auto meta2 = metadataFactory->getMetadata("Song1", "Artist1", "Album1");
   auto meta3 = metadataFactory->getMetadata("Song2", "Artist2", "Album2");
   meta1->display();
   meta2->display();
   meta3->display();

   return 0;
}

以上代码的输出如下 —

Playing playlist:
Playing MP3 file
Playing MP4 file
Playing MP4 file

Adding visual effects
Playing MP3 file
Enhancing audio quality
Access denied
Playing MP4 file
Playing using VLC implementation
Playing using Windows Media implementation
Title: Song1, Artist: Artist1, Album: Album1
Title: Song1, Artist: Artist1, Album: Album1
Title: Song2, Artist: Artist2, Album: Album2

何时使用结构型设计模式?

结构型设计模式在以下场景中很有用 −

  • 如果您想通过将复杂系统分解成更小、更易管理的部分来简化系统设计。
  • 当您认为需要通过创建易于理解和修改的对象与 class 之间关系来提升代码的灵活性和可维护性时。
  • 通过减少内存使用并改善对对象的访问来优化代码性能。
  • 当您想在不改变对象结构的情况下动态添加新功能到对象时。
  • 为复杂子系统提供简化的接口
  • 如果您想通过为其提供代理或占位符来控制对对象的访问。
  • 如果您想将抽象与实现分离,使两者能够独立变化。

结构型设计模式的优缺点

以下是结构型设计模式的一些重要优缺点 −

优点 缺点
改善代码组织。 可能引入复杂性。
增强灵活性和可维护性。 可能导致过度工程化。
促进代码复用。 可能影响性能。
减少内存使用。 可能需要额外学习。
为复杂系统提供清晰结构。 可能使调试更困难。

结论

在本章中,我们讨论了 C++ 中的结构型设计模式。我们看到了不同类型的结构型设计模式,以及它们如何用于创建灵活且可维护的代码。我们还看到了多个结构型设计模式如何结合使用来创建复杂系统的示例。结构型设计模式是任何软件开发者的重要工具,理解它们有助于您创建更好的软件。