Strukturmuster in Kürze
Gruppieren Sie Klassen oder Objekte zu einer größeren Struktur.
Dekorationsmuster: Fügen Sie einem Objekt dynamisch neue Funktionen hinzu.
Proxy-Modus: Stellen Sie einen Proxy für andere Objekte bereit, um den Zugriff auf dieses Objekt zu steuern.
Brückenmuster: Trennt die Abstraktion von ihrer Implementierung, sodass beide unabhängig voneinander variieren können.
Adaptermuster: Konvertieren Sie die Methodenschnittstelle einer Klasse in eine andere vom Client gewünschte Schnittstelle.
Zusammengesetzter Modus: Stellen Sie Objekte in einer Baumstruktur zusammen, um eine „Teil-Ganze“-Hierarchie darzustellen.
Darstellungsmodus: Bietet eine einheitliche Methode für den Zugriff auf eine Gruppe von Schnittstellen im Subsystem.
Fliegengewichtsmodus: Unterstützt effektiv eine große Anzahl feinkörniger Objekte durch gemeinsame Nutzung der Technologie.
Muster-C++-Code-Implementierung
Brückenmodus
Bridge Pattern (Bridge Pattern) ist ein strukturelles Entwurfsmuster, das verwendet wird, um den abstrakten Teil von seinem Implementierungsteil zu trennen, sodass sie unabhängig voneinander variieren können. Das Bridge-Muster erhöht die Skalierbarkeit und Flexibilität eines Systems, indem es eine mehrdimensionale Klassenhierarchie in zwei separate Vererbungshierarchien aufteilt.
Das Folgende ist ein einfaches Beispiel für die Verwendung von C++ zum Implementieren des Bridge-Modus, vorausgesetzt, wir möchten den Bridge-Modus für verschiedene Arten von Fernsehgeräten und Fernbedienungen implementieren:
#include <iostream>
// 实现部分
class TV {
public:
virtual void turnOn() = 0;
virtual void turnOff() = 0;
};
class SonyTV : public TV {
public:
void turnOn() override {
std::cout << "Sony TV is turned on" << std::endl;
}
void turnOff() override {
std::cout << "Sony TV is turned off" << std::endl;
}
};
class LGTV : public TV {
public:
void turnOn() override {
std::cout << "LG TV is turned on" << std::endl;
}
void turnOff() override {
std::cout << "LG TV is turned off" << std::endl;
}
};
// 抽象部分
class RemoteControl {
protected:
TV* tv;
public:
RemoteControl(TV* tv) : tv(tv) {}
virtual void turnOn() {
tv->turnOn();
}
virtual void turnOff() {
tv->turnOff();
}
};
class BasicRemoteControl : public RemoteControl {
public:
BasicRemoteControl(TV* tv) : RemoteControl(tv) {}
void turnOn() override {
std::cout << "Basic Remote: ";
RemoteControl::turnOn();
}
void turnOff() override {
std::cout << "Basic Remote: ";
RemoteControl::turnOff();
}
};
class AdvancedRemoteControl : public RemoteControl {
public:
AdvancedRemoteControl(TV* tv) : RemoteControl(tv) {}
void mute() {
std::cout << "Advanced Remote: Muted" << std::endl;
}
};
int main() {
TV* sonyTV = new SonyTV();
TV* lgTV = new LGTV();
RemoteControl* basicRemoteSony = new BasicRemoteControl(sonyTV);
RemoteControl* advancedRemoteLG = new AdvancedRemoteControl(lgTV);
basicRemoteSony->turnOn();
basicRemoteSony->turnOff();
advancedRemoteLG->turnOn();
advancedRemoteLG->mute();
advancedRemoteLG->turnOff();
delete sonyTV;
delete lgTV;
delete basicRemoteSony;
delete advancedRemoteLG;
return 0;
}
In diesem Beispiel TV
stellt die Klasse den Implementierungsteil dar SonyTV
und LGTV
ist dessen konkrete Implementierung. RemoteControl
Eine Klasse stellt einen abstrakten Teil dar BasicRemoteControl
und AdvancedRemoteControl
a ist seine konkrete Implementierung. Durch die Verwendung des Bridge-Modus können wir problemlos verschiedene Fernbedienungen mit unterschiedlichen Fernsehgeräten kombinieren und so die Entkopplung des abstrakten Teils und des Implementierungsteils realisieren.
Dekorationsmuster
Decorator Pattern (Decorator Pattern) ist ein strukturelles Designmuster, das das dynamische Hinzufügen von Funktionen zu Objekten ermöglicht, ohne die Struktur vorhandener Objekte zu ändern. Im Dekorationsmodus kann durch Erstellen einer Dekoratorklasse und Kombinieren der Dekoratorklasse mit der dekorierten Klasse die Überlagerung und Erweiterung von Funktionen realisiert werden.
Das Folgende ist ein einfaches Beispiel für die Verwendung von C++ zur Implementierung des Dekorationsmusters, vorausgesetzt, wir möchten verschiedene Kaffeesorten und die Funktion zum Hinzufügen von Gewürzen implementieren:
#include <iostream>
#include <string>
// 基础咖啡类(被装饰的类)
class Coffee {
public:
virtual std::string getDescription() const {
return "Basic Coffee";
}
virtual double cost() const {
return 1.0;
}
};
// 调料装饰器类
class CoffeeDecorator : public Coffee {
protected:
Coffee* coffee;
public:
CoffeeDecorator(Coffee* coffee) : coffee(coffee) {}
std::string getDescription() const override {
return coffee->getDescription();
}
double cost() const override {
return coffee->cost();
}
};
// 具体的调料装饰器类
class MilkDecorator : public CoffeeDecorator {
public:
MilkDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
std::string getDescription() const override {
return coffee->getDescription() + ", Milk";
}
double cost() const override {
return coffee->cost() + 0.5;
}
};
class SugarDecorator : public CoffeeDecorator {
public:
SugarDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
std::string getDescription() const override {
return coffee->getDescription() + ", Sugar";
}
double cost() const override {
return coffee->cost() + 0.2;
}
};
int main() {
Coffee* basicCoffee = new Coffee();
Coffee* milkCoffee = new MilkDecorator(basicCoffee);
Coffee* milkSugarCoffee = new SugarDecorator(milkCoffee);
std::cout << "Description: " << milkSugarCoffee->getDescription() << std::endl;
std::cout << "Cost: $" << milkSugarCoffee->cost() << std::endl;
delete basicCoffee;
delete milkCoffee;
delete milkSugarCoffee;
return 0;
}
In diesem Beispiel Coffee
repräsentiert die Klasse den zu dekorierenden Basiskaffee und CoffeeDecorator
die Klasse den Geschmacksdekorateur. Konkrete Gewürz-Decorator-Klassen wie MilkDecorator
und SugarDecorator
erben von CoffeeDecorator
und fügen die Beschreibung und den Preis des Gewürzs hinzu , indem sie die Methoden getDescription
und überschreiben. cost
Durch die Kombination verschiedener Dekoratoren können wir dem Kaffee zur Laufzeit dynamisch verschiedene Gewürze hinzufügen.
Adaptermuster
Adaptermuster (Adaptermuster) ist ein strukturelles Entwurfsmuster, das die Konvertierung inkompatibler Schnittstellen in von Clients erwartete Schnittstellen ermöglicht. Das Adaptermuster wird häufig verwendet, um zwei inkompatible Schnittstellen zu verbinden, damit sie zusammenarbeiten können.
Das Folgende ist ein einfaches Beispiel für die Verwendung von C++ zum Implementieren des Adaptermusters. Angenommen, wir haben eine vorhandene Audio-Player-Schnittstelle und eine neue MP3-Player-Klasse und müssen die MP3-Player-Schnittstelle an eine Audio-Player-Schnittstelle anpassen.
#include <iostream>
#include <string>
// 旧的音频播放器接口
class AudioPlayer {
public:
virtual void playAudio(const std::string& audioType, const std::string& fileName) = 0;
};
// 新的MP3播放器类
class Mp3Player {
public:
void playMp3(const std::string& fileName) {
std::cout << "Playing MP3 file: " << fileName << std::endl;
}
};
// 适配器类,将Mp3Player适配成AudioPlayer接口
class Mp3Adapter : public AudioPlayer {
private:
Mp3Player mp3Player;
public:
void playAudio(const std::string& audioType, const std::string& fileName) override {
if (audioType == "mp3") {
mp3Player.playMp3(fileName);
}
}
};
int main() {
AudioPlayer* audioPlayer = new Mp3Adapter();
audioPlayer->playAudio("mp3", "song.mp3");
audioPlayer->playAudio("wma", "song.wma"); // 这不会播放,因为适配器只支持MP3
delete audioPlayer;
return 0;
}
In diesem Beispiel AudioPlayer
handelt es sich um die alte AudioPlayer-Schnittstelle, Mp3Player
um die neue MP3-Player-Klasse Mp3Adapter
und um die Adapter-Klasse, die von einem Objekt erbt AudioPlayer
und Mp3Player
dieses enthält. Die Methode der Adapterklasse vergleicht den playAudio
eingehenden audioType
Parameter mit dem angepassten Format. Wenn es sich um ein „mp3“-Format handelt, rufen Sie Mp3Player
die Methode auf, um es abzuspielen.
Durch den Adaptermodus können wir die inkompatible MP3-Player-Schnittstelle an eine Audio-Player-Schnittstelle anpassen, ohne den vorhandenen Code zu ändern, sodass sie zusammenarbeiten können.
Darstellungsmodus
Facade Pattern ist ein strukturelles Entwurfsmuster, das eine einheitliche Schnittstelle für komplexe Subsysteme bereitstellt und es externen Clients erleichtert, das Subsystem zu verwenden, ohne seine interne komplexe Logik zu verstehen.
Das Folgende ist ein einfaches Beispiel für die Verwendung von C++ zum Implementieren des Darstellungsmodus. Angenommen, wir verfügen über ein komplexes elektronisches Gerätesystem, das Komponenten wie Fernseher, Audio und Beleuchtung umfasst, können wir den Darstellungsmodus verwenden, um eine einheitliche Schnittstelle zu erstellen und die Bedienung zu vereinfachen der Kunde.
#include <iostream>
// 子系统:电视
class TV {
public:
void turnOn() {
std::cout << "TV is on" << std::endl;
}
void turnOff() {
std::cout << "TV is off" << std::endl;
}
};
// 子系统:音响
class Stereo {
public:
void turnOn() {
std::cout << "Stereo is on" << std::endl;
}
void turnOff() {
std::cout << "Stereo is off" << std::endl;
}
};
// 子系统:灯光
class Lights {
public:
void turnOn() {
std::cout << "Lights are on" << std::endl;
}
void turnOff() {
std::cout << "Lights are off" << std::endl;
}
};
// 外观类:家庭影院
class HomeTheaterFacade {
private:
TV tv;
Stereo stereo;
Lights lights;
public:
void watchMovie() {
std::cout << "Get ready to watch a movie!" << std::endl;
tv.turnOn();
stereo.turnOn();
lights.turnOff();
}
void endMovie() {
std::cout << "Movie is over, shutting down..." << std::endl;
tv.turnOff();
stereo.turnOff();
lights.turnOn();
}
};
int main() {
HomeTheaterFacade homeTheater;
homeTheater.watchMovie();
std::cout << "----------------------" << std::endl;
homeTheater.endMovie();
return 0;
}
In diesem Beispiel stellen , TV
und Stereo
jeweils Lights
unterschiedliche Elektronik-Subsysteme dar. HomeTheaterFacade
Es handelt sich um eine Erscheinungsklasse, die den Betrieb jedes Subsystems kapselt und watchMovie
Methoden endMovie
zur einheitlichen Bedienung von Komponenten wie TV, Audio und Beleuchtung bereitstellt, sodass der Kunde das gesamte Heimkino einfacher bedienen kann.
Durch den Erscheinungsmodus kapseln wir das komplexe Subsystem in eine einfache Schnittstelle, sodass der Client die Komplexität des Subsystems nicht verstehen muss und nur das gesamte System über die Erscheinungsklasse bedienen muss.
Kombinationsmodus
Zusammengesetztes Muster (Composite Pattern) ist ein strukturelles Entwurfsmuster, das es ermöglicht, Objekte in einer Baumstruktur zu kombinieren, um eine „Teil-Ganze“-Hierarchie darzustellen. Das Composite-Muster ermöglicht es Clients, einzelne Objekte und Objektverbunde einheitlich zu verarbeiten, ohne ihre spezifischen Typen zu unterscheiden.
Das Folgende ist ein einfaches Beispiel für die Verwendung von C++ zur Implementierung des Verbundmodus, vorausgesetzt, wir möchten eine Dateisystemhierarchie erstellen, einschließlich Dateien und Ordner. Ordner können Dateien und andere Ordner enthalten und eine Baumstruktur bilden.
#include <iostream>
#include <vector>
// 组件抽象基类
class FileSystemComponent {
public:
virtual void display() const = 0;
};
// 文件类
class File : public FileSystemComponent {
private:
std::string name;
public:
File(const std::string& n) : name(n) {}
void display() const override {
std::cout << "File: " << name << std::endl;
}
};
// 文件夹类
class Folder : public FileSystemComponent {
private:
std::string name;
std::vector<FileSystemComponent*> components;
public:
Folder(const std::string& n) : name(n) {}
void addComponent(FileSystemComponent* component) {
components.push_back(component);
}
void display() const override {
std::cout << "Folder: " << name << std::endl;
for (const auto& component : components) {
component->display();
}
}
};
int main() {
File file1("file1.txt");
File file2("file2.txt");
File file3("file3.txt");
Folder folder1("folder1");
folder1.addComponent(&file1);
folder1.addComponent(&file2);
Folder folder2("folder2");
folder2.addComponent(&file3);
Folder root("root");
root.addComponent(&folder1);
root.addComponent(&folder2);
root.display();
return 0;
}
In diesem Beispiel FileSystemComponent
handelt es sich um die abstrakte Basisklasse der Komponente, einschließlich File
der Klassen und Folder
. File
Stellt eine Datei dar, Folder
stellt einen Ordner dar und ein Ordner kann Dateien und andere Ordner enthalten. Mithilfe des Kompositionsmodus können wir Dateien und Ordner in einer hierarchischen Struktur zusammenfassen, sodass der Client Komponenten verschiedener Ebenen einheitlich verarbeiten kann.
Mit dem Composite-Muster können wir problemlos komplexe Hierarchien erstellen und gleichzeitig eine einheitliche Schnittstelle zur Manipulation der gesamten Struktur verwenden. Dieser Modus ist besonders nützlich beim Umgang mit rekursiven Strukturen wie Dateisystemen, Organisationsstrukturen usw.
Proxy-Modus
Proxy-Muster (Proxy-Muster) ist ein strukturelles Entwurfsmuster, das ein Proxy-Objekt bereitstellt, um den Zugriff auf das Originalobjekt zu steuern. Ein Proxy-Objekt kann als Ersatz für das Originalobjekt verwendet werden, um den Zugriff auf das Originalobjekt zu steuern, zu verwalten oder zu verbessern.
Das Folgende ist ein einfaches Beispiel für die Verwendung von C++ zur Implementierung des Proxy-Modus. Angenommen, wir haben einen Bildlader, der Bilder von der Festplatte laden und anzeigen kann. Wir können den Proxy-Modus verwenden, um einen Proxy-Bildlader zu erstellen, der das Laden und Anzeigen von Bildern steuert.
#include <iostream>
#include <string>
// 抽象主题
class Image {
public:
virtual void display() = 0;
};
// 真实主题
class RealImage : public Image {
private:
std::string filename;
public:
RealImage(const std::string& file) : filename(file) {
loadFromDisk();
}
void display() override {
std::cout << "Displaying image: " << filename << std::endl;
}
void loadFromDisk() {
std::cout << "Loading image: " << filename << std::endl;
}
};
// 代理类
class ProxyImage : public Image {
private:
RealImage* realImage;
std::string filename;
public:
ProxyImage(const std::string& file) : filename(file), realImage(nullptr) {}
void display() override {
if (realImage == nullptr) {
realImage = new RealImage(filename);
}
realImage->display();
}
};
int main() {
Image* image = new ProxyImage("image.jpg");
// 图片未加载,调用display会加载并显示
image->display();
// 图片已加载,直接显示
image->display();
return 0;
}
In diesem Beispiel Image
ist es die abstrakte Themenschnittstelle, die die Anzeigemethode des Bildes definiert. RealImage
Es handelt sich um eine echte Themenklasse, die die Funktion des Ladens und Anzeigens von Bildern realisiert. ProxyImage
Es handelt sich um eine Proxy-Klasse, die eine Instanz eines echten Themas enthält, bei Bedarf den Zugriff auf das echte Thema erstellt und steuert.
Durch den Proxy-Modus können wir die Erstellung und das Laden des echten Themes verzögern und das Objekt wird erst dann tatsächlich erstellt und geladen, wenn es benötigt wird. Proxys können verwendet werden, um Funktionen wie Lazy Loading, Berechtigungskontrolle und Caching zu implementieren und so den Zugriff auf Originalobjekte zu verbessern oder zu steuern.
Fliegengewichtsmodus
Das Flyweight-Muster ist ein strukturelles Entwurfsmuster, das darauf ausgelegt ist, die Speichernutzung und den Aufwand für die Objekterstellung durch die gemeinsame Nutzung von Objekten zu minimieren. Es eignet sich für Szenen mit einer großen Anzahl ähnlicher Objekte. Durch die gemeinsame Nutzung dieser ähnlichen Teile kann der Speicherbedarf effektiv reduziert werden.
Das Folgende ist ein einfaches Beispiel für die Verwendung von C++ zur Implementierung des Flyweight-Modus. Angenommen, wir möchten einen Texteditor erstellen und müssen eine große Anzahl von Zeichenobjekten erstellen. Wir können das Flyweight-Muster verwenden, um Instanzen desselben Zeichens gemeinsam zu nutzen und so den Speicherverbrauch zu reduzieren.
#include <iostream>
#include <unordered_map>
// 字符类,享元类
class Character {
private:
char symbol;
public:
Character(char c) : symbol(c) {}
char getSymbol() const {
return symbol;
}
void display(int pointSize) {
std::cout << "Character: " << symbol << " Point size: " << pointSize << std::endl;
}
};
// 字符工厂,负责创建和管理字符实例
class CharacterFactory {
private:
std::unordered_map<char, Character*> characters;
public:
Character* getCharacter(char c) {
if (characters.find(c) == characters.end()) {
characters[c] = new Character(c);
}
return characters[c];
}
};
int main() {
CharacterFactory characterFactory;
// 创建并显示字符实例
Character* c1 = characterFactory.getCharacter('A');
c1->display(12);
Character* c2 = characterFactory.getCharacter('B');
c2->display(16);
// 再次获取相同字符实例
Character* c3 = characterFactory.getCharacter('A');
c3->display(20);
// 释放资源
delete c1;
delete c2;
return 0;
}
In diesem Beispiel Character
ist es die Flyweight-Klasse, die ein Zeichenobjekt darstellt. CharacterFactory
Es handelt sich um eine Zeichenfabrikklasse, die für die Erstellung und Verwaltung von Zeicheninstanzen verantwortlich ist. Durch die Verwendung des Flyweight-Musters können wir Instanzen desselben Zeichens gemeinsam nutzen und so den Speicherverbrauch reduzieren.
Der Schlüssel zum Flyweight-Muster besteht darin, den gemeinsam genutzten und den nicht gemeinsam genutzten Zustand des Objekts zu trennen. Der gemeinsam genutzte Zustand kann von mehreren Objekten gemeinsam genutzt werden, und der nicht gemeinsam genutzte Zustand kann über Parameter an das Objekt übergeben werden. Dadurch können die Objekterstellung und die Speichernutzung bis zu einem gewissen Grad reduziert werden.