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