Proxy Pattern

Proxy Pattern

Das Proxy Pattern ist ein strukturelles Entwurfsmuster, das einen Stellvertreter für ein anderes Objekt bereitstellt. Der Proxy kontrolliert den Zugriff auf das Originalobjekt und kann zusätzliche Funktionalitäten wie Sicherheitsprüfungen, Verzögerung oder Caching implementieren. Dies ermöglicht es, die Interaktion mit dem Originalobjekt zu optimieren oder zu steuern, ohne dessen ursprüngliche Implementierung zu verändern.

Was ist das Proxy Pattern?

Das Proxy Pattern bietet eine Möglichkeit, den Zugriff auf ein Objekt zu kontrollieren, indem ein „Proxy“ als Vermittler dient. Der Proxy agiert als Platzhalter und übernimmt die Verantwortung für Aufgaben wie Authentifizierung, Caching oder Lastverlagerung. Der Proxy hält eine Referenz zum echten Objekt und leitet die Aufrufe an das Originalobjekt weiter. Er kann jedoch auch vor oder nach dem Weiterleiten zusätzliche Logik ausführen.

Ein häufiges Beispiel für den Einsatz eines Proxy Patterns ist der Fall, in dem teure Ressourcen wie Datenbankverbindungen oder Netzwerkzugriffe genutzt werden. Der Proxy kann dann die teure Operation nur dann durchführen, wenn sie wirklich erforderlich ist.

Struktur des Proxy Patterns

  1. Subject: Die Schnittstelle, die sowohl vom echten Objekt als auch vom Proxy implementiert wird. Sie definiert die Methoden, die sowohl der Proxy als auch das tatsächliche Objekt anbieten.
  2. RealSubject: Das tatsächliche Objekt, das die Hauptoperationen ausführt. Es implementiert die Subject-Schnittstelle und enthält die Kernlogik.
  3. Proxy: Der Proxy implementiert ebenfalls die Subject-Schnittstelle und kontrolliert den Zugriff auf das RealSubject. Der Proxy kann zusätzliche Logik vor oder nach der Weiterleitung der Anfragen einfügen.

Beispiel des Proxy Patterns in C++

Im folgenden Beispiel simulieren wir einen Proxy für eine Ressource, die teuer in der Erstellung ist, etwa eine Bilddatei. Der Proxy lädt das Bild nur, wenn es wirklich benötigt wird.

#include <iostream>
#include <string>

// Subject: Die gemeinsame Schnittstelle, die sowohl vom Proxy als auch vom RealSubject implementiert wird
class Image {
public:
    virtual void display() const = 0;
    virtual ~Image() = default;
};

// RealSubject: Das echte Objekt, das die teure Operation ausführt
class RealImage : public Image {
private:
    std::string filename;

public:
    RealImage(const std::string& filename) : filename(filename) {
        loadImageFromDisk();  // Teure Operation: Bild laden
    }

    void loadImageFromDisk() const {
        std::cout << "Lade Bild: " << filename << std::endl;
    }

    void display() const override {
        std::cout << "Anzeige des Bildes: " << filename << std::endl;
    }
};

// Proxy: Der Proxy, der den Zugriff auf das echte Objekt kontrolliert
class ProxyImage : public Image {
private:
    RealImage* realImage;
    std::string filename;

public:
    ProxyImage(const std::string& filename) : realImage(nullptr), filename(filename) {}

    void display() const override {
        if (realImage == nullptr) {
            realImage = new RealImage(filename);  // Nur bei Bedarf wird das echte Bild geladen
        }
        realImage->display();
    }

    ~ProxyImage() {
        delete realImage;
    }
};

// Client-Code
int main() {
    Image* image1 = new ProxyImage("bild1.jpg");
    Image* image2 = new ProxyImage("bild2.jpg");

    image1->display();  // Bild wird nur beim ersten Aufruf geladen
    image2->display();  // Bild wird nur beim ersten Aufruf geladen
    image1->display();  // Bild wird nicht erneut geladen

    delete image1;
    delete image2;
    return 0;
}

Erklärung des C++-Beispiels

In diesem Beispiel haben wir die folgenden Komponenten:

  1. Subject: Die Image-Schnittstelle stellt die Methode display() bereit, die von beiden, dem Proxy und dem echten Objekt, implementiert wird.
  2. RealSubject: Die Klasse RealImage stellt die Methode display() zur Verfügung und führt die teure Operation aus, das Bild aus der Datei zu laden. Dies passiert im Konstruktor durch den Aufruf der Methode loadImageFromDisk(). Diese Methode ist teuer, da sie das Bild aus dem Dateisystem liest.
  3. Proxy: Die Klasse ProxyImage hält eine Referenz zu RealImage. Sie implementiert die Methode display(), lädt das Bild jedoch nur beim ersten Aufruf. Wenn der Proxy das erste Mal die Methode display() aufruft, wird das tatsächliche Bild durch die Instanziierung von RealImage geladen.

Der Proxy sorgt dafür, dass das Bild nur einmal geladen wird. Bei weiteren Aufrufen der Methode display() wird das Bild direkt angezeigt, ohne dass es erneut vom Festplattenspeicher geladen werden muss.

Vorteile des Proxy Patterns

  1. Leistung: Der Proxy kann den Zugriff auf teure Ressourcen optimieren, indem er diese nur bei Bedarf lädt. Dies verringert unnötige Operationen.
  2. Sicherheit: Der Proxy kann Authentifizierungsprüfungen oder Zugriffssteuerungen einführen, bevor er den Zugriff auf das Originalobjekt gewährt.
  3. Zugriffskontrolle: Der Proxy ermöglicht die Implementierung zusätzlicher Funktionen wie Caching, Lastverlagerung oder Protokollierung, ohne das eigentliche Objekt zu ändern.
  4. Flexibilität: Der Proxy kann in verschiedenen Szenarien eingesetzt werden, wie etwa zur Optimierung von Systemressourcen oder zur Bereitstellung zusätzlicher Funktionalitäten wie Lazy Loading.

Nachteile des Proxy Patterns

  1. Erhöhte Komplexität: Das Hinzufügen von Proxys kann den Code komplizierter machen, besonders wenn mehrere Proxys im System verwendet werden.
  2. Leistungseinbußen: In einigen Fällen kann der Proxy zusätzliche Verarbeitungszeit erfordern, da er eine zusätzliche Abstraktionsebene hinzufügt.
  3. Wartungsaufwand: Wenn viele Proxys verwendet werden, kann die Wartung des Systems schwieriger werden, da es mehrere Klassen gibt, die miteinander interagieren.

Fazit

Das Proxy Pattern ist ein nützliches Designmuster, das die Interaktion mit teuren oder schwer zugänglichen Objekten optimiert. Durch die Verwendung eines Proxys können teure Operationen wie das Laden von Daten nur bei Bedarf ausgeführt werden. Dies kann die Leistung eines Systems erheblich verbessern. Der Proxy bietet auch eine flexible Möglichkeit, zusätzliche Funktionalitäten wie Caching oder Sicherheitsprüfungen einzuführen, ohne das Originalobjekt zu ändern.

In C++ lässt sich das Proxy Pattern leicht implementieren, indem eine Proxy-Klasse erstellt wird, die das echte Objekt hält und den Zugriff darauf kontrolliert. Obwohl der Proxy das System flexibler und leistungsfähiger macht, sollte er mit Bedacht eingesetzt werden, um unnötige Komplexität und Leistungseinbußen zu vermeiden.

Zurück zur Liste der Pattern: Liste der Design-Pattern

VG WORT Pixel