| Design Pattern: Decorator |
|
| Development |
| Saturday, 03 December 2005 12:10 |
|
Okay, heute mal wieder einen technischen Eintrag zu den Möglichkeiten der Umsetzung von Design Patterns in Visual FoxPro. Der Eintrag erläutert die Implementierung des Decorator (deutsch: Dekorierer). Schauen wir uns zuerst die Definition von Wikipedia an: Die Instanz eines Dekorierers wird vor die zu dekorierende Klasse geschaltet. Der Dekorierer hat die gleiche Schnittstelle wie die zu dekorierende Klasse. Aufrufe an den Dekorierer werden dann verändert oder unverändert weitergeleitet (Delegation), oder sie werden komplett in Eigenregie verarbeitet. Der Dekorierer ist dabei "unsichtbar", da der Aufrufende gar nicht mitbekommt, dass ein Dekorierer vorgeschaltet ist. VFP bietet hier einen interessanten Lösungsansatz über die '_Access'-Funktionalität an. Es handelt sich dabei im eigentlichen Sinne um einen 'get'-Mechanismus, wie man es beispielsweise von Java respektive C# kennt. In VFP kann man nun eine Funktion namens 'This_Access' schreiben, welche permanent bei einem Zugriff auf das Objekt evaluiert wird. Es handelt sich dabei um einen impliziten Mechanismus, der ideal für das Decorator Design Pattern geeignet ist. Wichtig ist, dass man immer bei This_Access ein Objekt als Rückgabetyp liefert. Schliesslich würde 'This' immer einem Objekt entsprechen. Nun, unter Berücksichtigung dieser Annahmen implementieren wir mal einen ersten Entwurf für den Decorator: *========================================================================== Define Class Decorator As Session OlePublic *========================================================================== DataSession = 1 *--- Die Eigenschaft des Dekorierten kann, muss aber nicht, auf *--- Protected gesetzt werden. Es kommt ganz darauf an, ob man *--- dem Interface den Zugriff auf das 'interne' Objekt gestattet *--- oder nicht. Bei den OLEClass-Klassen von VFP steht das Decorated- *--- Objekt in der Eigenschaft This.Object zur Verfügung. *--- Protected oDecorated oDecorated = .Null. *--- Wir bieten zwar die Möglichkeit an, dass das dekorierte Objekt Function Destroy() As Boolean *--- Setzt das zu dekorierende Objekt zur Laufzeit. Über diese *--- Eigentliche Funktionalität des Decorators. Über die VFP-Funktionalität Sieht doch schon recht vernünftig und brauchbar aus, oder? Effektiv ist unser Decorator damit vollständig implementiert und wir können uns neuen Ufern zuwenden... :icon_cool: Wichtig bei der obigen Implementierung ist, dass die Eigenheiten des COM-Interface direkt berücksichtigt werden. Ich nenne es mal ein Dual-Interface für VFP-internen Nutzen und COM-Nutzen. Selbstverständlich kann man das COM-Interface noch mittels entsprechender _COMATTRIB-Definitionen vernünftig ausformulieren, aber diesen Task überlasse ich euch als Hausaufgaben. So, wozu kann man den Decorator überhaupt nutzen? Also, das Scatter-Objekt zur objektbasierten Repräsenz eines Datensatz hat ja schon einige Vorzüge, aber IMHO fehlt etwas extrem wichtiges: Methoden. Scatter Memo Name loScatter Nutzt man eine Ableitung des Decorator mit der gleichen respektive erweiterten Funktionalität. Hier zunächst mal die Klassendefinition des 'ScatterDecorator': *========================================================================== Define Class ScatterDecorator As Decorator *========================================================================== Procedure Scatter Scatter Name This.oDecorated Memo EndProc Procedure Gather That's all folks. Einfach eine Ableitung unserer abstrakten Decorator-Klasse und Methoden für die beiden Befehle Scatter und Gather erzeugt. Bereits jetzt haben wir eine identische Repräsentanz eines nativen Scatter-Objekts in einem Decorator - das Interface ist identisch! Okay, aber welchen echten Nutzen haben wir über diesen Weg? Ganz einfach... Unsere Klasse ScatterDecorator können wir beliebig mit weiteren Methoden erweitern. Ich nutze beispielsweise gerne eine Methode namens 'This.IsNew', die mir direkt Auskunft darüber gibt, ob ein Datensatz einen bestimmten Zustand inne hat. Oder denkbar wären weitere Informationen über die Tabellenstruktur, auf der das ScatterDecorator-Objekt basiert: This.TableAlias(), This.PrimaryKey(), This.Copy, This.Blank(), This.Next(), This.Previous(), etc... Ich hoffe, dass die Idee rübergekommen ist. Schauen wir uns nun mal die ScatterDecorator-Klasse im Einsatz an: *========================================================================== * Sample for ScatterDecorator *========================================================================== Use (Home(0) + "foxcode.dbf") Locate For Abbrev = "SCAN" oRec = Createobject('ScatterDecorator') Scatter Name oScatter Dimension myArr[3] Wie ihr sehen könnt, besteht keinerlei Unterschied zwischen den Ausgaben von oRec.Data und oScatter.Data. Der Decorator leitet die Aufrufe direkt durch und produziert daher das gleiche Resultat. Kleiner Nebeneffekt: Man spart sich die ganzen Optionen des Scatter-Befehls - :icon_biggrin: - Diese sind in der Klassendefintion des Decorators hinterlegt. Achja, nur so als Anregung... Ist doch ziemlich cool, wenn man dem Empty-Objekt ebenfalls Methoden verpassen kann, oder? Ich kann mir ehrlich gesagt nicht vorstellen, dass ihr bisher noch nie in die Situation gelaufen seid, dass man zwar ein Empty-Objekt nutzen will, sofern es Methoden besitzen würde... Oder? Auch hier ist das Decorator Design Pattern optimal geeignet: *========================================================================== Define Class EmptyDecorator As Decorator *========================================================================== *--- Überlagerung der Init-Methode, da wir direkt mit einem *--- Empty-Objekt als dekoriertem Objekt starten wollen. Falls *--- also kein Objekt reinkommt, erzeugen wir uns selbst eins. Function Init( toDecorated As Object ) As Boolean Local loDecorated m.loDecorated = m.toDecorated If Pcount() == 0 Or Not Vartype( m.loDecorated ) == 'O' Function IsValid() As Boolean Function AddProperty( tcMember As String, tuValue As Variant ) As Boolean Die Implementierung für einen EmptyDecorator ist sicherlich kontextspezifisch. Dennoch ergeben sich über diesen Ansatz weitere Freiheitsgrade in der Programmierung und vorallem effizienteren Nutzung von 'leeren' Objekten. Und man umgeht eine leidige Unzulänglichkeit von VFP: Aktuell ist keine Ableitung von Empty möglich: *========================================================================== Define Class Transfer As Empty *========================================================================== Das ist leider nicht möglich, aber bei der Nutzung des EmptyDecorator kommen wir dennoch in den gewünschten Bereich: *========================================================================== Define Class Transfer As EmptyDecorator *========================================================================== Yeah, let's rock the code... Ich hoffe, dass ich euch eine Idee über den theoretischen Aufbau, die Implementierung und den technischen Nutzen des Decorators in diesem Artikel vermitteln konnte. Mein Fazit in diesem Zusammenhang ist, dass dieses Pattern immens Vorteile bringen kann und bereits integrierter Bestandteil von Visual FoxPro - OleClass und OleBoundClass - ist. Sobald man also ActiveX-Controls in seinem Projekt benutzt, verwendet man bereits einen Decorator. :icon_cool: Über ein bisschen Feedback würde ich gerne freuen. Für welchen Einsatzzweck nutzt ihr einen Decorator oder würdet ihn nutzen?
PS: Ich muss die Klassen noch ein wenig ausformulieren, aber dann stehen diese als Attachment hier zur Verfügung. |





