23 Designmuster – 7 Strukturmuster

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 TVstellt die Klasse den Implementierungsteil dar SonyTVund LGTVist dessen konkrete Implementierung. RemoteControlEine Klasse stellt einen abstrakten Teil dar BasicRemoteControlund AdvancedRemoteControla 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 Coffeerepräsentiert die Klasse den zu dekorierenden Basiskaffee und CoffeeDecoratordie Klasse den Geschmacksdekorateur. Konkrete Gewürz-Decorator-Klassen wie MilkDecoratorund SugarDecoratorerben von CoffeeDecoratorund fügen die Beschreibung und den Preis des Gewürzs hinzu , indem sie die Methoden getDescriptionund überschreiben. costDurch 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 AudioPlayerhandelt es sich um die alte AudioPlayer-Schnittstelle, Mp3Playerum die neue MP3-Player-Klasse Mp3Adapterund um die Adapter-Klasse, die von einem Objekt erbt AudioPlayerund Mp3Playerdieses enthält. Die Methode der Adapterklasse vergleicht den playAudioeingehenden audioTypeParameter mit dem angepassten Format. Wenn es sich um ein „mp3“-Format handelt, rufen Sie Mp3Playerdie 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 , TVund Stereojeweils Lightsunterschiedliche Elektronik-Subsysteme dar. HomeTheaterFacadeEs handelt sich um eine Erscheinungsklasse, die den Betrieb jedes Subsystems kapselt und watchMovieMethoden endMoviezur 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 FileSystemComponenthandelt es sich um die abstrakte Basisklasse der Komponente, einschließlich Fileder Klassen und Folder. FileStellt eine Datei dar, Folderstellt 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 Imageist es die abstrakte Themenschnittstelle, die die Anzeigemethode des Bildes definiert. RealImageEs handelt sich um eine echte Themenklasse, die die Funktion des Ladens und Anzeigens von Bildern realisiert. ProxyImageEs 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 Characterist es die Flyweight-Klasse, die ein Zeichenobjekt darstellt. CharacterFactoryEs 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.

Acho que você gosta

Origin blog.csdn.net/yc7369/article/details/132416014
Recomendado
Clasificación