Posts mit dem Label Entwurfsmuster werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Entwurfsmuster werden angezeigt. Alle Posts anzeigen

Donnerstag, 5. Februar 2009

Einführung in Entwurfsmuster (Teil 5) / Introduction to design patterns (Part 5)

Im Dezember 2006/Januar 2007 schrieb Andy Kramek in seinem Blog über die Implementierung von Entwurfsmustern in Visual Foxpro. Andy hat mir erlaubt, meine deutsche Übersetzung seiner Artikelserie hier zu veröffentlichen. An dieser Stelle für seine Erlaubnis nochmals herzlichen Dank.

Heute nun Teil 5, der sich mit dem Dekorierer beschäftigt.

Hinweis: Die Überschriften der jeweiligen Kapitel sind direkt mit Andy's Originalbeitrag verknüpft.

Einführung in Entwurfsmuster (Design Patterns)
Von Andy Kramek

Der Dekorierer (The Decorator)

Der Dekorierer beschreibt eine Lösung zu dem Problem, ein Objekt in seiner Funktionalität zu erweitern, ohne den eigentlichen Code des Objektes zu verändern.

Wie erkenne ich einen Dekorierer?

Die formale Definition des Dekorierers gemäß „GoF“ lautet:
Erweitere ein Objekt dynamisch um Zuständigkeiten.
Der Bedarf, Funktionalitäten (oder Verhalten) eines Objektes dynamisch zu ändern tritt in zwei Situationen auf. Erstens, wenn der Quellcode eines Objektes schlicht nicht verfügbar ist, vielleicht handelt es sich um ein ActiveX-Control oder es kommt eine Klassenbibliothek eines Drittanbieters zum Einsatz. Zweitens, wenn die Klasse intensiv genutzt wird, die spezifische Verantwortlichkeit jedoch nur in ein oder zwei bestimmten Situationen benötigt wird und es nicht angebracht wäre, den Code der Klasse einfach hinzuzufügen.

In den vorangegangenen Beiträgen haben wir verschiedene Wege zu Feststellung der Steueraufschläge in Abhängigkeit der Lokation betrachtet. Das grundsätzliche Steuerberechnungsobjekt arbeitet in jedem Fall mit einer sichtbaren Methode namens CalcTax() welche zwei Parameter erwartet (einen Wert und eine Rate), und gibt den fälligen Steuerbetrag zurück. Wir bekamen das Grundproblem bezüglich des Umgangs mit den Steuern mit Hilfe des Brückenmusters in den Griff, indem wir die Implementierung von der Schnittstelle separierten. Obwohl es flexibler war als eine einfache direkte Codierung, stellten wir jedoch sehr schnell fest, dass es nicht flexibel genug war, um mit unterschiedlichen Steuerraten in unterschiedlichen Lokationen zurechtzukommen.

Sowohl die Strategie als auch die Zuständigkeitskette kommen mit dieser Anforderung zurecht, allerdings bedeuten beide Lösungswege das Klassenableitungen eingesetzt werden müssen. Das Dekorierer-Muster erlaubt es uns, dieses Problem ohne Ableitungen der Originalklasse zu lösen.

Stattdessen definieren wir ein neues Objekt mit gleichem Interface wie der Steuerrechner, welches jedoch den notwendigen Code beinhaltet, um die passende Steuerrate der gegebenen Lokation festzustellen. Dieses Objekt sieht genauso aus wie der Steuerrechner, es kann jedoch, weil es über eine Referenz auf den echten Steuerrechner verfügt, jedwede Berechnungsanfrage vorverarbeiten und anschließend einfach die echte Implementierung aufrufen.

Was sind die Komponenten eines Dekorierer?

Ein Dekorierer stellt zwei grundsätzlich Anforderungen. Zunächst einmal benötigen wir die Implementierungsklasse, welche die Schnittstelle und die grundlegende Funktionalität definiert. Desweiteren benötigen wir einen Dekorierer der die Schnittstelle der Implementierung reproduziert und über eine Referenz auf diese verfügt. Das Klient-Objekt leitet nun Aufrufe die eigentlich direkt an die Implementierung gegangen wären, an das Dekorierer Objekt. Die Basisstruktur für das Dekorierer-Muster sieht wie folgt aus:

 
Eigentlich ist dieses Muster eine erweiterte Brücke. Soweit es den Klienten betrifft kann er den Dekorierer aufrufen, als ob es sich um die Implementierung am Ende einer Standardbrücke handelt, da beide Schnittstellen identisch sind. Soweit es die Implementierung betrifft sieht der Aufruf genauso aus, wie wenn er direkt vom Klienten gekommen wäre. Letztlich muss sie nicht einmal wissen, dass es den Dekorierer überhaupt gibt.

Wie implementiere ich einen Dekorierer?

Eine neue Klasse mit dem Namen ‚cntDekorator‘ wird definiert um das Dekorierer-Beispiel zu implementieren. Es hat zwei spezielle Eigenschaften und eine spezielle Methode wie im folgenden beschrieben:

Name          Beschreibung
cImpClass     Name der für diesen Dekorierer zu instanziierenden Klasse
cImpLib       Klassenbibliothek der zu instanziierenden Implementierungsklasse
oCalculator   Objektreferenz auf eine Instanz der real implementierten Klasse
              der CalcTax() Methode
              Dieses Objekt wird innerhalb der INIT() Methode des Dekorierers
              instanziiert.
CalcTax          Die Implementierung des Dekorierers für die äquivalente Methode
              der realen Implementierung.


Eigentlich ist die Dekorierer-Klasse sehr einfach aufgebaut. Wenn sie erzeugt wird instanziiert sie die reale Implementierungsklasse, welche über die Namens- und Bibliotheks-Eigenschaften definiert ist. Zusätzlich implementiert sie eine operationale Methode (in diesem Fall ‚CalcTax‘), welche genauso benahmt ist und über dieselbe Signatur wie die reale Methode verfügt.

Der Code in der CalcTax()-Methode des Dekorierer ist ziemlich gradlinig. Er erwartet zwei Parameter, die LokationsID als String und den Preis für den die Steuer berechnet werden soll. Beachten Sie, dass dies nicht dieselben Parameter sind, die von der CalcTax() Methode der realen Klasse benötigt werden (dort übergeben wir Preis und Steuerrate). Die CalcTax() Methode des Dekorierers stellt die passende Rate basierend auf der LokationsID fest und ruft dann die CalcTax() Methode des Implementierungsobjekts mit den realen Parameters auf. Der Rückgabewert wird unverändert einfach nur an den Klienten durchgereicht.

LPARAMETERS tcLocation, tnPrice

LOCAL lnRate, lnTax
STORE 0 TO lnRate, lnTax
*** Korrekte Steuerrate feststellen
DO CASE
CASE tcLocation = [01]
    lnRate = 5.75
CASE tcLocation = [02]
    lnRate = 5.25
CASE tcLocation = [03]
    lnRate = 0.00
OTHERWISE
    lnRate = 0
ENDCASE

*** Nun die reale Implementierung aufrufen
lnTax = This.oCalculator.CalcTax( tnPrice, lnRate )

*** Und das Ergebnis zurückgeben
RETURN lnTax

Um den Dekorierer nutzen zu können müssen wir nur eine kleine Änderung im Klienten durchführen. Anstatt die reale Implementierung zu instanziieren muss nun das Dekorierer Objekt instanziiert werden. Denken Sie daran, dass die Aufrufsignatur des Dekorierers mit der realen Methode identisch ist. Somit müssen keine weiteren Änderungen durchgeführt werden.

LPARAMETERS tcContext, tnPrice
LOCAL lnTax
WITH ThisForm
    *** Pruefen, ob der Dekorierer verfuegbar ist
    IF VARTYPE( This.oCalc ) # [O]
        *** Ist er nicht, also erzeugen wir ihn
        .oCalc = NEWOBJECT( [cntDecorator], [calclass.vcx] )
    ENDIF
    *** Nun einfach die CalcTax() Methode aufrufen und Lokation und Preis uebergeben
    lnTax = .oCalc.CalcTax( tcContext, tnPrice )
    IF ISNULL( lnTax )
        *** Die Daten konnten nicht verarbeitet werden
        MESSAGEBOX( [Unable to Process this Location], 16, [Failed] )
        lnTax = 0
    ENDIF
    RETURN lnTax
ENDWITH

Obwohl es sich hier um ein sehr einfaches Beispiel handelt, können Sie sehen, wie leicht es ist, die Funktionalität mit Hilfe eines Dekorierers zu erweitern. Eine typisches ‚real life‘ Problem, das mit Hilfe dieses Musters angegangen werden kann ist, wie man implementierungsspezifische Validierungen bei generischen Speicherroutinen unterstützt.

Dekorierer Zusammenfassung

Das Dekorierer Muster kommt zum Einsatz, wenn wir das grundsätzliche Verhalten einer spezifischen Instanz modifizieren möchten, ohne dabei entweder eine Klassenableitung zu erzeugen, oder den Originalcode zu ändern. Letztlich agiert der Dekorierer als Prä-Prozessor der Implementierung wobei er sich zwischen Klient und Implementierung schaltet.

Quellennachweis:

Entwurfsmuster - Elemente wiederverwendbarer objektorientierter Software ADDISON-WESLEY ISBN 3-89319-950-0

Montag, 12. Januar 2009

Einführung in Entwurfsmuster (Teil4) / Introduction to design patterns (Part4)

Im Dezember 2006/Januar 2007 schrieb Andy Kramek in seinem Blog über die Implementierung von Entwurfsmustern in Visual Foxpro. Andy hat mir erlaubt, meine deutsche Übersetzung seiner Artikelserie hier zu veröffentlichen. An dieser Stelle für seine Erlaubnis nochmals herzlichen Dank.

Heute nun Teil 4, der sich mit der Zuständigkeitskette beschäftigt.

Hinweis: Die Überschriften der jeweiligen Kapitel sind direkt mit Andy's Originalbeitrag verknüpft.

Einführung in Entwurfsmuster (Design Patterns)
Von Andy Kramek

Die Zuständigkeitskette (Chain of Responsibility)

Was ist eine Zuständigkeitskette und wie setze ich sie ein?

Im vorangegangenen Kapitel wurde mit dem Strategiemuster eine Lösung aufgezeigt wie mit verschiedenen alternativen Implementierungen zur Laufzeit umgegangen werden kann. Die Zuständigkeitskette beschreibt ein alternatives Muster das dieses Problem von einer anderen Seite aus beleuchtet.

Wie erkenne ich, wann ich eine Zuständigkeitskette benötige?

Die formale Definition der Zuständigkeitskette gemäß „GoF“ lautet:
Vermeide die Kopplung des Auslösers einer Anfrage mit seinem Empfänger, indem mehr als ein Objekt die Möglichkeit enthält, die Aufgabe zu erledigen. Verkette die empfangenden Objekte und leite die Anfrage an der Kette entlang, bis ein Objekt sie erledigt.
Im vorangegangenen Beispiel habe ich aufgezeigt, wie eine Strategie eingesetzt wird, um ortsspezifische Mehrwertsteuersätze zu verarbeiten. Hierbei haben wir jedoch gesehen, dass zur Implementierung einer Strategie irgendein Objekt zur Laufzeit entscheiden muss, welche der möglichen Unterklassen implementiert werden soll. Dies dürfte nicht immer erwünscht oder gar möglich sein.

Innerhalb einer Zuständigkeitskette kann jedes Objekt selbst beurteilen, wie es eine Anfrage zu verarbeiten hat und, sollte es die Anfrage nicht selbst verarbeiten können, weiß es nur, wie diese an ein anderes Objekt weitergereicht wird, und somit haben wir eine Kette. Die Konsequenz daraus ist, dass der Klient (welcher die Aktionsanfrage initiiert) somit nur noch das erste Objekt innerhalb der Kette kennen muss. Darüber hinaus muss jedes Objekt innerhalb der Kette nur das nächste Glied der Kette kennen. Die Zuständigkeitskette kann sowohl als vordefinierte (statisch) oder dynamische Kette (zur Laufzeit erzeugt jedes Objekt im Bedarfsfall seinen eigenen Nachfolger) implementiert werden.

Welches sind die Komponenten einer Zuständigkeitskette?

Eine Zuständigkeitskette kann durch die Erzeugung einer abstrakten Steuerungsklasse (‚Handler‘) implementiert werden, welche das Interface und die generischen Funktionalitäten spezifiziert, und durch anschließendes Erzeugen der konkreten Unterklassen um die diversen möglichen Implementierungen zu definieren. Es ist jedoch keinesfalls eine starre Vorgabe für die Definition aller Glieder einer Zuständigkeitskette, dass diese von derselben Klasse abgeleitet sein müssen um sicherzustellen, dass sie alle das über notwendige Interface zu Integration anderer Kettenglieder verfügen. Klienten Objekte benötigen eine Referenz auf die spezifische Unterklasse, welche ihren individuellen Einstiegspunkt in die Kette darstellt. Dies bedeutet jedoch wiederum nicht, dass alle Klienten denselben Einstiegspunkt benutzen müssen.

 
Und wieder können wird das grundsätzliche Brückenmuster erkennen. Dies liegt daran, dass jede Verknüpfung innerhalb der Kette letztlich eine Brücke zwischen einer Abstraktion und einer Implementierung darstellt. Der einzige Unterschied zur simplen Brücke liegt darin, dass jedes einzelne Objekt in Abhängigkeit seiner jeweiligen Situation, beide Rollen übernehmen kann.

Wie implementiere ich eine Zuständigkeitskette?

Die Frage, die ich im Zusammenhang mit dem Strategiemuster gestellt habe war „Wie gehe ich die Berechnung der Umsatzsteuer an, wenn die Berechnung vom Verkaufsort abhängt“. Sie werden sich erinnern, dass die Lösung so aussah, dass spezielle Unterklassen definiert wurden um jede Steuerrate gezielt verarbeiten zu können. Im Anschluss wurde dann auf Basis einer Tabelle entschieden, welche Unterklasse für welchen Ort benötigt wird.

Um dasselbe Problem mit einer Zuständigkeitskette zu lösen können wir die ursprüngliche Steuerberechnungsklasse heranziehen, die wir für die Strategie definiert haben und fügen die folgenden Eigenschaften hinzu:

cCanHandle  Definiert den Kontext der mit dieser Klasse verarbeitet werden kann
cNextObj    Name des als nächstes zu instanziierenden Objektes in der Kette
cNextObjLib Klassenbibliothek des nächsten Objektes in der Kette
oNext       Objektreferenz des nächsten Objektes in der Kette

Die Eigenschaft ‚cCanHandle‘ definiert den ‚Kontext‘ den die spezielle Instanz akzeptiert, wohingegen die ‚Next‘-Eigenschaften genutzt werden, um das nachfolgende Objekt zu definieren. Dieses könnte zum einen vorbesetzt sein, um eine vordefinierte Kette zu erzeugen, oder wir könnten sogar zur Laufzeit die relevanten Werte festlegen um eine frei erweiterbare sich den Bedürfnissen anpassende Kette zu erzeugen.

Stellen wir uns beispielsweise einmal vor, dass als Kontext des ersten Objektes eine 7.00%ige Steuerrate definiert ist. In diesem Fall könnte dieses Objekt feststellen, dass bei einer übergebenen Steuerrate die über dem eigenen Kontext liegt, es keinen Sinn macht, ein Objekt zu instanziieren, dass eine niedrigere Steuerrate verarbeitet. Wie implementieren wir so etwas? Eine Möglichkeit wäre, eine Tabelle mit einer Liste der Kontexte der relevanten Klassen (und Bibliotheken) vorzuhalten. Dann könnte jedes Objekt in der Kette einfach die benötigte Information nachschauen.

Zusätzlich zu den bereits ober beschriebenen Eigenschaften benötigen wir mindestens zwei Methoden:

ProcessRequest: Sichtbare Methode die genutzt wird um das Objekt aufzurufen
                und die entscheidet ob eine spezielle Anfrage durch das Objekt
                verarbeitet wird.
CalcTax:        Die eigentliche Methode welche die Berechnung durchführt und
                das Ergebnis zurückgibt.

Das Klient Objekt muss entweder über eine Referenz auf das erste Objekt der Kette verfügen oder diese erzeugen können. Es wird dann die ProcessRequest()-Methode des Objektes aufrufen und sowohl den Kontext als auch den Verkaufspreis übergeben, dessen Mehrwertsteuer benötigt wird. Der folgende Code geht davon aus, dass die Zuständigkeitskette entweder einen gültigen Mehrwertsteuerbetrag oder NULL zurück gibt:

WITH ThisForm
    * Prüfen, ob das erste Objekt der Kette verfügbar ist
    IF VARTYPE( This.oCalc ) # [O]
        * Objekt muss erzeugt werden
        .oCalc = NEWOBJECT( [TaxChain01], [chor.vcx] )
    ENDIF
    * Nun die ProcessRequest Methode aufrufen und sowohl Kontext als auch Preis übergeben
    lnTax = .oCalc.ProcessRequest( cContext, nSalePrice )
    IF ISNULL( lnTax )
        * Berechnung konnte nicht durchgeführt werden
        MESSAGEBOX( [Lokation konnte nicht berechnet werden], 16, [Fehlgeschlagen] )
        lnTax = 0
    ENDIF
    RETURN lnTax
ENDWITH

Der Code innerhalb der ProcessRequest Methode ist innerhalb der abstrakten Klassendefinition hinterlegt und komplett generisch. Er entscheidet darüber, ob eine ankommende Berechnungsanforderung lokal abgearbeitet werden kann. Ist das der Fall ruft er einfach die Methode CalcTax() auf (ebenfalls innerhalb der abstrakten Klasse definiert und verarbeitet den übergebenen Preis und die eingebettete Steuerrate). Kann die Berechnung nicht lokal durchgeführt, so werden hängt die nachfolgende Aktion davon ab, ob ein anderes Objekt definiert und verfügbar ist um die Anforderung entgegenzunehmen:

LPARAMETERS tcContext, tnPrice
LOCAL lnTax
WITH This
    * Können wir die Anforderung verarbeiten?
    lcCanHandle = CHRTRAN( .cCanHandle, ['], [] )
    IF tcContext == lcCanHandle
        * Ja, also die Standardmethode CalcTax aufrufen und den Preis übergeben
        lnTax = .CalcTax( tnPrice )
    ELSE
        * Nein, können wir nicht. Haben wir ein definiertes Folgeobjekt?
        IF !EMPTY( .cNextObj ) AND !EMPTY( .cNextObjLib )
            * Ja, haben wir. Aber existiert es schon?
            IF VARTYPE( This.oNext ) # [O]
                * Objekt erzeugen
                .oNext = NEWOBJECT( .cNextObj, .cNextObjLib )
            ENDIF
            * Objekt aufrufen
            lnTax = .oNext.ProcessRequest( tcContext, tnPrice )
        ELSE
            * Kein Folgeobjekt definiert, also NULL zurückliefern
            lnTax = NULL
        ENDIF
    ENDIF
    RETURN lnTax
ENDWITH

Mehr Code wird nicht benötigt und die individuellen Unterklassen für dieses sehr einfache Beispiel benötigen letztlich überhaupt keinen individuellen Code, da alles über die Einstellungen der Eigenschaften gesteuert wird (inklusive der Eigenschaft nTaxRate welche in der original Basisklasse definiert ist).

Wann sollten wir eine Kette anstelle einer Strategie benutzten?

An diesem Punkt könnten Sie vielleicht denken, „Warum soll ich mir darüber Gedanken machen?“ – insbesondere da wir bereits eine perfekte, einfache datengetriebene Lösung für das Problem im Strategiemuster gesehen haben. Nun, die Beschränkung des Strategiemusters ist, dass nur EINE Unterklasse existieren kann und deswegen ist es nicht sinnvoll, wenn wir mehrere Operationen benötigen. Das hier gezeigte Beispiel benutzte eine Ein-Schuss-Kette! Sobald ein Objekt die Anforderung bearbeiten kann wird ein Ergebnis zurückgeliefert und es wird kein weiteres Objekt mehr benötigt.

Die Zuständigkeitskette ergibt sich immer dann, wenn unter Umständen mehrere Operationen notwendig sind. Eine Erweiterung unseres einfachen Steuerproblems könnte die Verarbeitung weiterer Aufgaben wie ‚Versandkosten und Bearbeitungsgebühren‘, ‚Rabatte‘ oder sogar mehrere Steuersätze (bspw. örtliche Preisaufschläge) sein.

In solchen Situationen reicht eine Strategie nicht aus. Die Zuständigkeitskette verarbeitet sie jedoch problemlos. Alles was benötigt wird ist, anstatt beim ersten Objekt, das die Anforderung bearbeiten kann, zu stoppen, die Anforderung an jedes weitere Objekt in der Kette eindeutig weiterzureichen und jedem die Möglichkeit zu geben, an der endgültigen Lösung zu partizipieren. Natürlich kann es sein, dass wir bei einem solchen Szenario mehr als einen Rückgabewert benötigen, aber Parameterobjekte stellen eine einfache und zuverlässige Möglichkeit dar, dies zu handhaben.

Und nun hätten wir in unserer Kette Objekte die auf der Berechnung der „Mehrwertsteuersätze“, der „Versandkosten“ und der „Rabatte“ basieren. Alles was sie benötigen sind die relevanten PEMs (Eigenschaften –Properties, Ereignisse –Events, Methoden –Methods) um innerhalb der Kette aktiv zu werden.

Zuständigkeitskette Zusammenfassung

Die Zuständigkeitskette offeriert uns einen anderen Weg zur Lösung des Problems, Funktionalitäten bereitzustellen ohne diese explizit ausprogrammieren zu müssen. Der größte Vorteil der Zuständigkeitskette liegt vielleicht in der einfachen Erweiterbarkeit. Der Hauptnachteil ist hingegen das dramatische Ansteigen der aktiven Objekte im System. Wie immer, zum Abschluss, möchte ich daran erinnern, dass auch wenn die jeweiligen Implementierungsdetails variieren können, sich das Muster nicht verändern wird.

Quellennachweis:

Entwurfsmuster - Elemente wiederverwendbarer objektorientierter Software ADDISON-WESLEY ISBN 3-89319-950-0

Donnerstag, 18. Dezember 2008

Einführung in Entwurfsmuster (Teil3) / Introduction to design patterns (Part3)

Im Dezember 2006/Januar 2007 schrieb Andy Kramek in seinem Blog über die Implementierung von Entwurfsmustern in Visual Foxpro. Andy hat mir erlaubt, meine deutsche Übersetzung seiner Artikelserie hier zu veröffentlichen. An dieser Stelle für seine Erlaubnis nochmals herzlichen Dank.

Heute nun Teil 3, der sich mit dem Strategiemuster beschäftigt.

Hinweis: Die Überschriften der jeweiligen Kapitel sind direkt mit seinem Originalbeitrag verknüpft.

Einführung in Entwurfsmuster (Design Patterns)
Von Andy Kramek

Die Strategie (Strategy)

Was ist eine Strategie und wie setze ich sie ein?

Die Strategie beschreibt eine Lösung zum Hauptproblem beim Erstellen von generischem Code – Wie gehe ich mit nicht vorhergesehenen Anforderungen und Änderungen bei der Implementierung um.

Woran erkenne ich, wo ich eine Strategie benötige?

Die formelle Definition einer Strategie, gemäß „Entwurfsmuster – Elemente wieder verwendbarer objektorientierter Software“ von Gamma, Helm, Johnson und Vlissides lautet:
Definiere eine Familie von Algorithmen, kapsele jeden einzelnen und mache sie austauschbar. Das Strategiemuster ermöglicht es, den Algorithmus unabhängig von ihn nutzenden Klienten zu variieren.
Beim ersten Lesen hört sich das ein klein wenig obskur an. Wir sollten uns ein einfaches Beispiel ansehen, bei dem ein Strategiemuster helfen könnte. Nehmen wir das Problem, die Mehrwertsteuer für einen Einkauf zu berechnen. Wir können davon ausgehen, dass unsere Applikation weiß, welche Gegenstände verkauft wurden und die entsprechende Menge (auf Basis der Preisinformationen) und so feststellen kann, wie hoch der Einkaufsbetrag ist. Nun müssen wir die Mehrwertsteuer ausweisen bzw. berechnen.

Eigentlich gibt es da kein Problem, solange wir in einer Region leben, die nur einen Mehrwertsteuersatz kennt. Worauf zu achten ist: Wird der Gegenstand überhaupt versteuert oder nicht, und wenn ja wie ist sein augenblicklicher Mehrwertsteuersatz. Allerdings sind in den USA Mehrwertsteuersätze länder- oder sogar ortsspezifisch. Der Prozentsatz hängt davon ab, wo ich etwas kaufe (oder sogar, in einigen Fällen, wo der Sitz des Verkäufers liegt).

Beispielsweise beläuft sich in unserem Staat die Mehrwertsteuer für Kleidung auf 5.75%, aber wenn wie 20 Meilen nach Süden fahren beträgt der Steuersatz nur 5.25% und wenn wir in den nächsten Staat fahren (nur 50 Meilen entfernt) dann gibt es dort gar keine Mehrwertsteuer auf Kleidung. Ein und derselbe Gegenstand der mit $29.95 ausgeschildert ist kann uns deswegen $31.67, $31.52 oder $29.95 kosten, je nach dem, wo wir ihn kaufen.

Um das ganze etwas abstrakter zu Beschreiben, die ausgewiesene Mehrwertsteuer der Transaktion hängt vom Kontext ab, in dem die Transaktion stattfindet. Würden wir nun den entsprechenden Code in der Applikation hinterlegen könnte das wie folgt aussehen:

DO CASE

CASE lcLocale = [Akron]

    IF lcItemType = [Kleidung]

        lnTaxRate = 5.75

    ELSE

        * Weitere Einträge hier

    ENDIF

CASE lcLocale = [Canton]

    IF lcItemType = [Kleidung]

        lnTaxRate = 5.25

    ELSE

        * Weitere Einträge hier

    ENDIF

CASE lcLocale = [Grove City]

    IF lcItemType = [Kleidung]

        lnTaxRate = 0.00

    ELSE

        * Weitere Einträge hier

    ENDIF

OTHERWISE

    * Standardwert zuweisen

    lnTaxRate = 5.50

ENDCASE


Wir können sofort erkennen, was das Problem sein wird!

Was passiert, wenn wir Cleveland zu unserer Ortsliste hinzufügen, oder wenn Akron beschließt, seine Steuer zu senken, um Canton Verkaufsanteile streitig zu machen. Wir müssen unseren Code mit allen damit verbundenen Risiken ändern. Je mehr Orte wir der Liste hinzufügen umso unübersichtlicher wird der Code.

Natürlich könnten wir die Informationen innerhalb einer Tabelle hinterlegen (vielleicht eine Spalte für Orte, eine Spalte für jeden Artikel) und lesen diese Daten jedes Mal nach wenn wir sie benötigen. Auf diese Weise müssten wir nur noch einzelne Datensätze anpassen wenn sich Werte ändern, allerdings könnte das ziemlich schnell eine ganz schön große Tabelle werden und es gäbe jede Menge Duplikate und Redundanzen innerhalb dieser Datenmenge. Also auch dies wäre nicht die optimale Lösung.

Was wir doch eigentlich in unserer Applikation tun wollen ist, einfach den Artikelpreis und Kontextinformationen zu übergeben, und zwar an etwas, das uns einfach nur meldet wie hoch der Mehrwertsteuerbetrag ist. Mit anderen Worten wollen wir die Abstraktion (berechne die fällige Mehrwertsteuer) von der Implementierung (basierend auf dem Ort) trennen. Solange wir nur den Code verschieben, wird nur das Problem verlagert aber nicht gelöst.

Eine einfache Brücke ist nicht die Lösung, denn sie ermöglich nur eine einzelne Implementierung und wir benötigen multiple Implementierungen. Was wir brauchen ist die Möglichkeit zur Laufzeit zu entscheiden, welche Implementierung zum Einsatz kommen soll. Das Strategiemuster bietet uns die Möglichkeit, Klassen für jede Situation zu definieren mit der wir konfrontiert werden und dann die passende zur Laufzeit zu instanzieren.

Was sind die Komponenten einer Strategie?

Eine Strategie besteht aus drei essentiellen Komponenten. Die „abstrakte Strategie“ definiert das Interface (Schnittstelle) sowie generelle Funktionalitäten. Die „konkreten Strategien“ sind die Unterklassen welche die verschiedenen möglichen Implementierungen definieren. Die dritte Komponente, der „Kontext“, ist verantwortlich für die Referenzierung auf die aktuelle Implementierung. Die Strategie wird über eine Anfrage zur Aktion durch den Klienten initiiert.

 
Wir können uns eine Strategie als „dynamische Brücke“ vorstellen, in welcher das eine Ende (der Kontext) statisch ist, das andere (die Strategie) jedoch aus einer Auswahl von möglichen Varianten eine Implementierung auswählt, die für den Augenblick passend ist. Die Essenz des Musters ist, das die Entscheidung, welche Unterklasse instanziert werden soll, zu jeder Zeit davon abhängt, welche Informationen vom Klienten (bspw. der Applikation) benötigt werden.

Typischerweise weist das Muster die Verantwortung zur Instanzierung der konkreten Strategie dem Klienten zu, welcher dann eine Referenz an den Kontext übergibt. Um nun ein Strategiemuster zu implementieren müssen Sie eine abstrakte Strategieklasse definieren, und so viele unterschiedliche spezielle Unterklassen wie Sie benötigen. Das Objekt, welches üblicherweise die Rolle des Kontext übernimmt (in VFP ist dies üblicherweise die Form oder der Parent Container) muss dem potentiellen Klient ein passendes Interface bereitstellen. Ebenso benötigt es eine Eigenschaft die genutzt wird, um die Referenz auf das aktuelle Implementierungsobjekt aufzunehmen.

Wie implementiere ich eine Strategie?

Das folgende Beispiel zeigt auf, wie wir ein Strategiemuster als Lösung für das zuvor aufgeführte Mehrwertsteuerproblem einsetzen könnten.

Das Erste was wir tun müssen ist, dass wir eine abstrakte Klasse definieren, die über eine Eigenschaft verfügt, in der die zu verarbeitende Steuerrate hinterlegt wird. Sie verfügt zudem über eine Methode zum Berechnen der Steuer auf Basis des übergebenen Betrages und der Steuerrateneigenschaft. Nun erzeugen wir so viele Unterklassen dieser Definition wie wir Steuerratenvarianten haben – jede Variante eine Unterklasse.

Jetzt müssen wir noch herausfinden, wie wir feststellen können, wann welche der diversen Unterklassen zum Einsatz kommen soll. Um dies zu können müssen wir die verschiedenen Lokationen und verfügbaren Steuerraten mit der passenden Unterklasse in Relation setzen.

Natürlich gibt es viele Möglichkeiten dies zu tun und die einfachste wäre, einfach eine Liste der Lokationen (bspw. Städte) und die dort geltende Steuerrate zu erstellen. Da wir eine Unterklasse für jede Steuerrate haben können wir diese auf Basis der zur Lokation gehörenden Steuerrate herausfinden. Da wir jedoch immer noch eine Tabelle mit einem Datensatz je Stadt benötigen, können wir den Aufwand dadurch reduzieren, dass wir unsere Unterklasse analog zur Steuerrate benamen. Dies würde in etwa wie folgt aussehen:

Stadt      Steuerrate   Unterklasse
Akron      5,75         Tax575
Canton     5,25         Tax525
Grove City 0,00         Tax000
Kent       5,75         Tax575

Mit anderen Worten nutzen wir die zugehörige Steuerrate zur Identifikation der Unterklasse. Dies hat zur Konsequenz, dass wir die dritte Spalte der Tabelle nicht mehr benötigen. Wir können die Unterklasse direkt auf Basis der Steuerrate benennen.

Wie codieren wir dies nun in einem Formular? Nun, das ist ziemlich einfach. Alles was wir brauchen ist eine DropDownListe mit den Lokationen aus unserer Stadt/Steuerraten-Tabellen. Wenn die Stadt ausgewählt wurde, erhalten wir die zugehörige Steuerrate. Hierüber wiederum generieren wir den Namen der zu instanzierenden Unterklasse, und da alle Unterklassen aus einer zentralen Basisklasse abgeleitet sind, wissen wir, dass alles was wir nur die passende Methode im Objekt mit den richtigen Parametern - in diesem Fall zwei Parameter – den Kontextschlüssel (in diesem Fall die zugeordnete Steuerrate) und den Preis zu dem die Steuern berechnet werden sollen. Der Kontextschlüssel wird genutzt um den Namen der benötigten Unterklasse zu generieren. Wenn die aktuelle Klasse nicht die richtige ist, instanzieren wir einfach die korrekte Klasse.

Hier nun der hinterlegte Code:

LPARAMETERS tcContext, tnPrice
WITH This
    * Definiere den Namen des Strategieobjektes basierend auf der Basisklasse und Steuerrate
    lcStrategy = [CNTSTRAT] + PADR( tcContext, 3, [0] )
    * Gibt es die Klasse schon?
    IF ISNULL( .oStrategy ) OR NOT UPPER( .oStrategy.Name ) == lcStrategy
        * Diese Klasse muss noch erzeugt werden
        .oStrategy = NEWOBJECT( lcStrategy, [Ch15.vcx] )
    ENDIF
    * Jetzt wird die Berechnungsmethode aufgerufen
    lnTax = .oStrategy.CalcTax( tnPrice )
    RETURN lnTax
ENDWITH

Dies ist nur eine Möglichkeit wie eine Strategie implementiert werden kann. Sie macht im gegebenen Beispiel durchaus Sinn, in anderen Szenarien können andere Implementierungen notwendig sein. Stellen Sie sich beispielsweise einmal die Zuweisung von Rabatten an Aufträge vor. Die Maske zur Erfassung (Der Kontext) könnte diverse unterschiedliche Rabatte innerhalb eines Auftrags benötigen:
  • Mengenrabatte, zu berechnen für einen einzelnen Artikel
  • Auftragswertrabatt, zu berechnen auf den kompletten Auftrag
  • Spezialrabatte oder Werberabatte auf einzelne Artikel
  • Kundenbezogene Rabatte auf den kompletten Auftrag
Ein mögliches Interface könnten Commandbuttons nutzen (Die Klienten) so dass der Operator entscheiden kann, wann welcher spezielle Rabatt zugewiesen werden soll. In diesem Szenario wäre es durchaus sinnvoll und auch einfacher, jeder Button kann die Referenz auf eine spezielle Rabattierungs-Strategie-Unterklasse an die Form übergeben (Hierbei sollten wir bedenken, dass diese Vorgehensweise zwar in VFP möglich ist, in anderen Sprachen geht dies jedoch nicht). Die Form könnte die die jeweils benötigte Kalkulation durchführen und das Ergebnis anzeigen, so dass der Code nicht in den Button reinkopiert werden müsste.

Strategiemuster Zusammenfassung

Wir haben eine mögliche Implementierung für ein Strategiemuster gesehen und ein anderes kurz angerissen. Der Vorteil einer Strategie ist, dass sie es umgeht, alternative Implementierungen innerhalb des Codes hinterlegen zu müssen. Sie erlaubt es uns, separate Objekte die über ein gemeinsames Interface verfügen zu erzeugen. Für jede Option erfolgt eine gezielte Instanzierung des benötigten Objekts. Wie bei anderen Mustern auch gilt, dass die jeweiligen Implementierungen verschieden sein können, das Muster sich jedoch nicht ändert.

Quellennachweis:
Entwurfsmuster - Elemente wiederverwendbarer objektorientierter Software ADDISON-WESLEY ISBN 3-89319-950-0

Mittwoch, 10. Dezember 2008

Einführung in Entwurfsmuster (Teil2) / Introduction to design patterns (Part2)

Im Dezember 2006/Januar 2007 schrieb Andy Kramek in seinem Blog über die Implementierung von Entwurfsmustern in Visual Foxpro. Andy hat mir erlaubt, meine deutsche Übersetzung seiner Artikelserie hier zu veröffentlichen. An dieser Stelle für seine Erlaubnis nochmals herzlichen Dank.

Heute nun Teil 2, der sich mit dem grundlegendsten Muster - der Brücke - beschäftigt.

Hinweis: Die Überschriften der jeweiligen Kapitel sind direkt mit seinem Originalbeitrag verknüpft.

Einführung in Entwurfsmuster (Design Patterns)
Von Andy Kramek

Die Brücke (Bridge)

Das erste Muster, das wir uns anschauen werden ist die Brücke, oft auch als die ‚Mutter aller Entwurfsmuster’ bezeichnet. Sie ist das grundlegendste aller Muster und je mehr Sie sich in Muster im Allgemeinen einarbeiten, werden Sie feststellen, dass sie irgendwie in fast allen anderen Mustern enthalten ist.

OK, was also ist eine Brücke?

Die formelle Definition einer Brücke, gemäß „Entwurfsmuster – Elemente wieder verwendbarer objektorientierter Software“ von Gamma, Helm, Johnson und Vlissides (GoF) lautet:
Entkoppelt eine Abstraktion von ihrer Implementierung, so dass beide unabhängig voneinander variiert werden können.
Imposant, oder? Machen wir das in unserem Code tatsächlich?

Die kurze Antwort ist, dass wir das wahrscheinlich öfter tun als wir glauben. Nehmen wir einmal ein allgemeines Beispiel. Wenn wir Code innerhalb unserer Formulare und Klassen schreiben, dann möchten wir Fehler abfangen, und üblicherweise (als pflichtbewusste Entwickler) unseren Anwendern sagen, wenn ein Problem auftritt. Die offensichtlichste und simpelste Lösung ist es ein paar Zeilen Code in der betroffenen Methode zu hinterlegen. Das Ergebnis könnte wie folgt aussehen:

IF NOT < eine Funktion die TRUE/FALSE zurückliefert >

    lcText = [ Das Feld enthält keinen korrekten Wert ] + CHR(13);

           + [ Drücken sie eine beliebige Taste für eine erneute Eingabe ]

    WAIT lcText WINDOW

    RETURN .F.

ENDIF


In unserer gesamten Applikation werden wir auf Situationen stoßen, in denen wir ein „wait window“ wie oben codiert benötigen um den Anwender zu informieren. Dies wird auch so lange perfekt funktionieren bis eines von zwei Dingen eintritt. Entweder beschließt unser Anwender, dass er diese mickrigen „Wait Windows“ hasst und viel lieber eine Messagebox im Windowsstil hätte, oder viel schlimmer, wir müssen die Applikation in eine Umgebung portieren, in der „wait window“ nicht unterstützt wird. Vielleicht als COM-Objekt oder in einem Web-Formular oder das Form wird als Toplevel Form mit Desktop = .T. gesetzt.

Wie auch immer. Nun sind wir gezwungen, unsere Applikation zu durchsuchen und überall den besagten Code an die neuen Anforderungen umzustellen. Ich weiß nicht wie es bei Ihnen ist, aber meine Erfahrung sagt mir, dass die Chance, beim ersten Mal alles richtig umzusetzen (kein Auftreten übersehen und jede einzelne Umstellung fehlerfrei), gleich Null ist. Und selbst wenn dies der Fall sein sollte bleibt uns noch die Arbeit des Prüfens jeder einzelnen Umstellung.

Ein anderes Szenario tritt ein, wenn wir mit Kollegen arbeiten. Stellen Sie sich vor, in einem meiner Formulare rufe ich eine Systemfunktion auf, und da ich ein vorsichtiger Entwickler bin prüfe ich, ob das Ergebnis wie erwartet geliefert wurde und im Fehlerfall blende ich eine Meldung wie die folgende ein:

lcText = [ Fehlerhafter Rückgabewert bei Funktionsaufruf ]

Zwischenzeitig hat mein Kollege ein ähnliches Stück Code in einem anderen Teil der Applikation geschrieben und bringt im Fehlerfall die folgende Meldung:

lcText = [ Die angeforderte Aufgabe konnte nicht durchgeführt werden ]

Auch wenn wir beide eigentlich auf die gleiche Art und Weise reagieren ist der Inhalt unterschiedlich, obwohl in beiden Fällen das Selbe passiert ist. Verwirrend? Darauf können Sie wetten. Schlimmer noch, für den Anwender sieht es unsauber aus.

Nun, was hat das alles mit dem Brückenmuster zu tun? Der Grund für unser Problem ist, dass wir nicht bemerkt haben, dass wir eigentlich eine Abstraktion (Anzeigen einer Nachricht für den Anwender) an eine Implementierung (die Befehle „Wait Window“ oder „Messagebox“) binden. Hätten wir es bemerkt, würden wir wohl eine Brücke genutzt und das Problem umgangen haben. Hier nun derselbe Code unter Einsatz eines Brückenmusters:

IF NOT <eine Funktion die TRUE/FALSE zurückliefert >

    This.oMsgHandler.ShowMessage( 9011 )

    RETURN .F.

ENDIF

Sehen Sie den Unterschied? Wir wissen nicht, oder es interessiert uns nicht, welche Art Nachricht angezeigt wird oder was im Hintergrund passiert. Alles was wir wissen müssen ist, wo wir die Referenz auf das Objekt herholen, dass die Aufgabe für uns erledigt. Und natürlich den Identifier der dem Handler mitteilt, welche Nachricht wir zu Anzeige bringen möchten. Natürlich ist es essentiell wichtig, dass alle Handler auch über das passende Interface verfügen, in diesem Fall die ShowMessage() Methode.

Es ist die Herkunft der Referenz die „Brücke“ genannt wird. In diesem Beispiel stellt die „oMsgHandler“-Eigenschaft die Brücke dar zwischen dem Code der eine Nachricht verlangt und dem Mechanismus der sich um die Nachricht kümmert. Alles was wir nun tun müssen, um die Art wie die Nachricht verarbeitet wird zu ändern, ist die Objektreferenz in der Eigenschaft zu ändern. Dies kann recht einfach zur Laufzeit durchgeführt werden, hängt jedoch von der Umgebung ab, unter der das Parentobjekt instanziert wurde (der Mechanismus der sich dahinter verbirgt ist ein Beispiel für ein anderes Muster mit dem Namen „Strategie“ (Strategy), das ich später noch ansprechen werde). Dieser Ansatz entkoppelt erfolgreich die Abstraktion von seiner Implementation und als Ergebnis erhalten wir erheblich besser wieder verwendbaren Code.

Was sind die Komponenten einer Brücke?

Eine Brücke besteht aus zwei essentiellen Komponenten, der „Abstraktion“, welche das verantwortliche Objekt für die Instanzierung der Operation darstellt, sowie der „Implementierung“ bei der es sich um das Objekt handelt, das diese ausführt. Die Abstraktion kennt ihre Implementierung entweder weil sie über eine Referenz darauf verfügt, oder weil es sie besitzt (bspw. enthält). Auch wenn es passieren kann, dass die Abstraktion die Implementierung erzeugt, so ist dies keine absolute Voraussetzung für dieses Muster. Eine Brücke kann auch eine bereits existierende Referenz zu einem Implementierungsobjekt nutzen. Tatsache ist, dass dieser Art des Einsatzes von Brücken sehr effizient sein kann. Verschiedene Abstraktionsobjekte können dasselbe Implementierungsobjekt einsetzen.

Eine weitere Tatsache ist, da Visual FoxPro über ein sehr gutes Containership Modell verfügt, so dass die meisten Entwickler feststellen werden, dass sie (unabsichtlich) schon mehr Brücken implementiert haben als sie glaubten. Die meisten ‚Manager’ Objekte (bspw. Form Manager, Ini Datei Manager, Message Manager) sind letztlich Beispiele für das Brückenmuster.



Wichtig ist, eine simple Nachrichtenweiterleitung nicht mit einer Brücke zu verwechseln. Wenn eine Methode eines Command buttons eine Methode seiner Form aufruft in der die entsprechende Aktion implementiert ist, dann handelt es sich um eine einfache Nachrichtenweiterleitung und nicht um eine Brücke. Ruft diese Formularmethode jedoch ein anderes Objekt zum Durchführen der Aktion auf, dann haben wir eine Brücke.

Interessanterweise tritt eine andere Möglichkeit auf, wenn ein Form (oder ein Container) über eine Eigenschaft verfügt, die eine Referenz auf ein Implementierungsobjekt hält. Ein enthaltenes Objekt (bspw. ein Command button auf einer Form) kann direkt auf diese Property zugreifen und eine lokale Referenz auf das Implementierungsobjekt erhalten. Hierdurch kann es direkt Methoden des Implementierungsobjektes aufrufen. Nun, handelt es sich hierbei um eine Brücke oder um Nachrichtenweiterleitung?

Tatsächlich könnten wir beide Varianten rechtfertigen. Allerdings bringt uns die Antwort nicht weiter. Dieses Problem tritt nämlich nur in Visual FoxPro auf, da es standardmäßig Eigenschaften als „Public“ bereitstellt und gleichzeitig rückwärtige Zeiger implementiert, die es Objekten erlauben, ihr Parentobjekt direkt zu adressieren. In den meisten objektorientierten Entwicklungsumgebungen ist dies schlicht und ergreifend nicht möglich, auf solche Weise auf andere Objekte zuzugreifen.

Um eine Brücke zu implementieren müssen Sie somit wissen, welches Objekt die Rolle der Abstraktion und welches die Rolle der Implementierung übernimmt. Haben sie diese erst einmal definiert, dann bleibt nur noch die Entscheidung, wie die Brücke zwischen den beiden codiert werden soll. Dies hängt dann letztlich komplett vom jeweiligen Szenario ab.

Quellennachweis:

Entwurfsmuster - Elemente wiederverwendbarer objektorientierter Software ADDISON-WESLEY ISBN 3-89319-950-0

Donnerstag, 4. Dezember 2008

Einführung in Entwurfsmuster (Teil1) / Introduction to design patterns (Part1)

Im Dezember 2006/Januar 2007 schrieb Andy Kramek in seinem Blog über die Implementierung von Entwurfsmustern in Visual Foxpro. Andy hat mir erlaubt, meine deutsche Übersetzung seiner Artikelserie hier zu veröffentlichen. An dieser Stelle für seine Erlaubnis nochmals herzlichen Dank.

Heute nun also der erste Teil.

Hinweis: Die Überschriften der jeweiligen Kapitel sind direkt mit seinem Originalbeitrag verknüpft.

Einführung in Entwurfsmuster (Design Patterns)
Von Andy Kramek

Warum sich mit Entwurfsmustern plagen?

Entwurfsmuster sind eine Standardsprache für das Erkennen, Definieren und Beschreiben von Lösungen für Softwareproblemstellungen. Die Kenntnis von Entwurfsmustern erleichtert das Verstehen von vorhandenen Systemen sowie die Anforderungsbeschreibung von komplexen neuen Systemen.

Ich erinnere mich, das ich vor einigen Jahren mit Paul Maskens in der Lobby des Lindner Kongresshotels in Frankfurt saß, während er versuchte, mir eine Idee für eine Werkzeugkiste innerhalb einer Gridspalte zu erklären. Er beschrieb seine Lösung zu diesem Problem und plötzlich, nach ca. 20 Minuten, stellte ich fest, dass er über ein Strategiemuster sprach. Hätte er seine Ausführungen mit dem Hinweis begonnen, dass er ein Strategiemuster implementiert hatte, dann hätte ich umgehend die generellen Anforderungen und Ansätze verstanden, die er zur Umsetzung angewandt hatte. Dies hätte eine Menge Zeit und Aufwand gespart und es uns beiden erlaubt auf die Details der Implementation direkt einzugehen.

Auf jeden Fall ist es wichtig zu erkennen, das Entwurfsmuster selbst nicht die Lösung spezifischer Probleme darstellen. Sie sind einfach nur ein Weg zur Identifizierung von Problemen und beschreiben generische Lösungen die sich als erfolgreiche Vorgehensweisen etabliert haben. Die wirkliche Implementierung eines Entwurfsmusters ist immer noch Aufgabe des Entwicklers.

Was ist ein Entwurfsmuster?

Bevor wir uns den speziellen Beispielen zuwenden, die sich mit der Implementierung von Entwurfsmustern unter Visual FoxPro befassen, sollte ich mit der Definition von ‚Entwurfsmustern’ beginnen. Diverse Definitionen wurden schon angeboten und die wohl am meisten zitierte Version stammt aus dem Buch „Entwurfsmuster – Elemente wieder verwendbarer objektorientierter Software“ von Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides (ursprünglich auch als ‚Die Vierer Gang -> Gang of Four’ oder einfacher GoF bekannt). Sie bieten im ersten Kapitel (Was ist ein Entwurfsmuster) ihres Buches die folgende Definition: Dies ist eine gute Definition denn sie beinhaltet die vier Schlüsselelemente eines jeden Entwurfsmusters.
Ein Entwurfsmuster benennt, abstrahiert und identifiziert die relevanten Aspekte einer allgemeinen Entwurfsstruktur. Diese Aspekte beschreiben, warum das Muster für die Entwicklung eines wieder verwendbaren objektorientierten Entwurfs nützlich ist. Das Entwurfsmuster identifiziert die teilnehmenden Klassen und Objekte, die Rollen welche sie spielen, die Interaktionen zwischen den Rollen und die ihnen zugeteilten Aufgaben.
Dies ist eine gute Definition denn sie beinhaltet die vier Schlüsselelemente eines jeden Entwurfsmusters.

- Es hat einen Namen.
Dies ist wichtig, denn es erlaubt dem Entwickler eines der fundamentalen Probleme im Softwaredesign zu umgehen, nämlich wie kommuniziere ich mit anderen über das, was ist tue. Das Beispiel mit Paul was ich zuvor angesprochen habe spiegelt dies perfekt wieder.

- Es abstrahiert ein Problem.
GoF bezeichnet dies als ‚ Absicht’ (Intent). Es sagt uns sowohl die Natur des Problems als auch die Lösung durch ein Entwurfsmuster. Nehmen wir zum Beispiel das Problem, zu verhindern dass Anwender mehrere neue Instanzen einer Applikation erzeugen können. Normalerweise versuchen Anwender neue Instanzen einer Applikation zu starten, weil sie das Ursprungsfenster verkleinert und vergessen haben, dass die Anwendung bereits gestartet wurde. Wir können sofort erkennen, dass es sich hierbei um das Singelton-Muster handelt, denn dieses hat die Absicht sicher zu stellen, dass von einer Klasse nur eine Instanz existiert und dass es einen zentralen Aufrufpunkt gibt.

- Es definiert eine Designstruktur.
Es ist wichtig zur erkennen, das Entwurfsmuster keine Lösung für ein gegebenes Problem liefern. Sie beschreiben Strukturen, die es uns erlauben, das Problem zu lösen, und das in einer Weise, die eher eine Wiederverwendung ermöglicht, als die althergebrachte Art, seinen Code zu schreiben. Wir haben alle schon einmal die Erfahrung gemacht, dass wir einen bestimmten Code bereits an einer anderen Stelle eingesetzt haben, wir ihn jedoch nicht wiederverwenden konnten, weil wir den Code nicht von der Umgebung isoliert hatten. Zweck von Entwurfsmustern ist es, solche Situationen zu erkennen und sie somit zu vermeiden.

- Es identifiziert die Verteilung von Zuständigkeiten.
Dies ist natürlich der Schlüssel aller Designanforderungen und ist nicht auf Entwurfsmuster beschränkt. Letztlich ist, sobald wir wissen was eine Klasse (oder Objekt) zu tun hat, das schreiben des Codes ein Leichtes.

Der Vorteil eines Entwurfsmusters ist, dass wenn wir das Problem erst einmal erkannt haben und wir es einem Muster zuordnen können, es uns aufzeigt, wie die Verantwortlichkeiten zugewiesen werden. Es hilft uns so, schnell die beste Lösung zu erzeugen. Es ist nicht meine Absicht, sämtliche Entwurfsmuster an dieser Stelle ausschweifend zu betrachten (dem widmen sich komplette Bücher). Aber in dieser kleinen Serie werde ich die gängigsten Muster abdecken und zeigen, wie Visual FoxPro eingesetzt werden kann, um musterbasierende Lösungen zu erstellen. Jedes Kapitel ist in sich geschlossen und deckt jeweils ein Muster ab, aber ich werde mich um eine allgemeine Beschreibung bemühen.

Quellennachweis:

Entwurfsmuster - Elemente wiederverwendbarer objektorientierter Software ADDISON-WESLEY ISBN 3-89319-950-0