Freitag, 23. Januar 2009

Reparierte VFP9 SP2 Hilfedatei wird unter VFPX bereitgestellt / Fixed VFP9 SP2 help file is provided under VFPX

Gute Nachricht von Rick Schummer zur im letzten Jahr im April erschienenen defekten SP2 Hilfedatei.

Nachdem diverse (noch nicht genannte) Entwickler in den letzten Monaten die Hilfedatei durch Decompilieren, Reparieren und Neukompilieren von ihren ursprünglichen Problemen befreit haben, hat YAG von Microsoft Seite nun die Genehmigung erteilt, die Hilfedatei auf VFPX unter der Creative Common License zu veröffentlichen. Diese ermächtigt die VFP Community Verbesserungen an der Hilfedatei vorzunehmen.

Rick kündigt an, dass sobald die letzten Fehler bereinigt sind, die Datei auf VFPX bereitsteht.

Donnerstag, 15. Januar 2009

WAV-Sounds mit VFP abspielen / Playing WAV-sounds with VFP

Vor einiger Zeit hatte ich mir einen kleinen TeaTimer gebaut, damit mein schwarzer Tee nicht dauernd 10 bis 20 Minuten zieht.

Das Ganze passte auch prima, solange ich die Timer-Form im Auge behielt. Dummerweise schaue ich während der Arbeit mit Foxpro aber auf mein Codefenster und so passierte es recht häufig, dass ich doch wieder einen 20 Minuten Tee da stehen hatte.

Lange Rede kurzer Sinn, es musste etwas 'ohrenfälligeres' her, denn auch das Flackern des Timers bei abgelaufener Zeit half nicht wirklich.

Nachdem also der Entschluss zum Abspielen einer WAV-Datei gefallen war, musste ich feststellen, dass mir das Beispiel in den Solution Samples nicht auf Anhieb die Informationen preisgab, die mich interessierten. Irgendwo im Web fand ich dann ein Stück Mustercode zum Aufrufen der MCI API-DLL, welches ich als Grundlage für den u.a. Beispielcode verwendet habe. Seitdem funktionieren auch meine 3 und 4 Minuten Tees... ;-)

Die notwendige API befindet sich im Windows-System32 Verzeichnis und heisst WinMM.dll. Sie beinhaltet das Media Control Interface aus dem letztlich nur die Funktion PlaySound deklariert werden muss.

Die u.a. Funktion PlayWAV kann mit oder ohne Parameter aufgerufen werden. Wird der Name einer WAV-Datei übergeben, so wird versucht, diese abzuspielen. Andernfalls wird eine Standarddatei aus dem Windows Media Verzeichnis abgespielt. Die möglichen Werte des zweiten Parameters können dem u.a. DEFINE-Block entnommen werden.

?PlayWAV()

FUNCTION PlayWAV

    LPARAMETERS vWavFile as String, vFlags as Integer

    LOCAL llReturn as Boolean
    llReturn = .T.

    * // -----------------------------------------------------------
    * // vWavFile = (optional)                                      
    * //             Pfad und Dateiname der abzuspielen WAV-Datei  
    * //                                                          
    * // vFlag    = (optional)                                      
    * //            gezielte Abspielvariante gem. u.a. DEFINEs      
    * // -----------------------------------------------------------

    #DEFINE SND_SYNC         0x00000000    && synchron abspielen (default)                  
    #DEFINE SND_ASYNC        0x00000001    && asynchron abspielen                          
    #DEFINE SND_NODEFAULT    0x00000002    && silence (!default) wenn WAV nicht gefunden  
    #DEFINE SND_MEMORY       0x00000004    && zeigt auf eine im RAM befindliche Datei      
    #DEFINE SND_LOOP         0x00000008    && solange abspielen bis neue WAV übergeben wird
    #DEFINE SND_NOSTOP       0x00000010    && derzeit ablaufende Sounds nicht unterbrechen  
    #DEFINE SND_NOWAIT       0x00002000    && nicht warten wenn der Treiber 'busy' ist      
    #DEFINE SND_ALIAS        0x00010000    && Name ist ein registrierter ALIAS              
    #DEFINE SND_ALIAS_ID     0x00110000    && Alias ist eine vordefinierte ID              
    #DEFINE SND_FILENAME     0x00020000    && Name ist ein Dateiname                      
    #DEFINE SND_RESOURCE     0x00040004    && Name ist eine Ressource oder Atom          
    #DEFINE SND_PURGE        0x00000040    && entferne zum Abspielen nicht-statische Tasks  
    #DEFINE SND_APPLICATION  0x00000080    && Suche nach assoziierter Applikation          

    * // WinMM.dll -> MCI API-DLL -> MCI = Media Control Interface  
    TRY
        DECLARE INTEGER PlaySound IN WinMM.dll AS PLAYSOUNDOVERAPI ;
                STRING @ pszSound, INTEGER HMODULE_hmod, LONG DWORD_fdwSound

        m.vWavFile    = IIF( ;
                          VARTYPE(m.vWavFile) <> [C] OR !FILE(m.vWavFile), ;
                          ADDBS(GETENV([windir])) + [media\tada.wav], ;
                          m.vWavFile ;
                         )
        m.vFlags    = IIF( ;
                          VARTYPE(m.vFlags)=[N], ;
                          m.vFlags, ;
                          SND_ASYNC+SND_FILENAME ;
                         )
        PLAYSOUNDOVERAPI(m.vWavFile,0,m.vFlags)
    CATCH
        * // API Aufruf schlug fehl, also nix mit TADA              
        llReturn = .F.
    ENDTRY

    RETURN llReturn

ENDFUNC

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