Cold Fusion MX & Objekte
Warenkorb
Einleitung
Cold Fusion MX & Objekte
Cold Fusion Components (CFC) stellen einen der zentralen Bausteine von Macromedias MX-Strategie dar. Einerseits sind CFCs der Server-seitige Anknüpfungspunkt für Rich-Internet-Applications mit Flash-Frontends, andererseits spielen sie eine wichtige Rolle in der Entwicklung HTML-basierter Anwendungen mit Cold Fusion. In diesem Umfeld dienen sie zur Trennung von Präsentations- und Applikations-Code und somit zum Aufbau einer modularen und skalierbaren Software-Architektur.
Plattformen und Sprachen wie Java beziehungsweise J2EE oder .NET folgen dem objektorientierten Paradigma, das Mechanismen wie Kapselung, Wiederverwendung, Generalisierung und Polymorphismus unterstützt.
Cold Fusions Tag-Sprache CFML ist per se keine vollständig objektorientierte Sprache im Sinn akademischer Definitionen. Allerdings bietet das Konzept der CFCs die Möglichkeit, mit CFML-Objekten zu arbeiten.
Ziel des Workshops ist es, das Grundgerüst einer Warenkorb-Komponente für die Verwendung in Shop-Applikationen zu entwickeln. Sobald ein Benutzer einen Einkaufsvorgang startet, wird eine Instanz dieser Komponente für ihn verfügbar gemacht und im Speicher gehalten, bis er zur virtuellen Kasse schreitet. Den vollständigen Code finden Sie wie gewohnt auf der Heft-CD.
CFC-Basics
Cold Fusion MX & Objekte
Cold Fusion MX unterscheidet zwischen so genannten Static und Instance-based CFCs. Bei statischen CFCs handelt es sich lediglich um eine Sammlung von Methoden, die mehr oder weniger strukturiert in einer Komponente gebündelt sind. Bei instanzbasierten CFCs kommt als weiteres Feature die Kapselung aus der OO-Welt hinzu: Eine solche CFC besteht aus Daten und Methoden, die auf den Daten operieren. Über definierte Interfaces werden Methoden angeboten, die den Zustand einer Instanz der CFC manipulieren können. Die Daten oder auch Eigenschaften der Instanz sind vor direkten Zugriffen von außen und somit vor willkürlicher Manipulation geschützt. Für die Erstellung einer Cold-Fusion-Component sind vier CFML-Tags besonders wichtig:
– CFCOMPONENT: Dieses Tag definiert den Rahmen einer CFC und umschließt deren Code.
– CFFUNCTION: Das Tag umschließt und deklariert eine Methode einer CFC. Innerhalb dieses Tags ist die Business-Logik der Methode eingebettet.
– CFARGUMENT und CFRETURN: Beide Tags dienen der Übergabe beziehungsweise der Rückgabe von Parametern in oder aus Methoden.
Mit diesem Wissen ist es nun möglich, einfache, statische CFCs zu erstellen, die gegebenenfalls thematisch sortiert Methoden bündeln und als Dienstleistung bereitstellen. In der Regel handelt es dabei um zustandslose Cold Fusion Components.
CFC mit Zustands-Instanzen
Cold Fusion MX & Objekte
Instanzen von CFCs, die Objekte der realen oder virtuellen Welt repräsentieren sollen, haben zu jedem Zeitpunkt ihrer Existenz einen klar definierten Zustand. Dieser wird durch ihre Datenfelder – oft auch als Eigenschaften oder Propertys bezeichnet – beschrieben.
Datenfelder in Cold Fusion Components sind einfach durch Variablendeklaration innerhalb der Komponente erzeugbar. Dies geschieht wie in gewöhnlichen CFML-Seiten mit Hilfe des Tags CFSET oder innerhalb eines CFSCRIPT-Blocks durch Zuweisung eines Wertes an einen innerhalb der Namenskonventionen von CFML frei wählbaren Variablennamen.
Bei der Arbeit mit Datenfeldern für Cold Fusion Components ist zu beachten, dass es innerhalb einer CFC zwei besondere Gültigkeitsbereiche für Variablen gibt, die sich wesentlich voneinander unterscheiden.
Der so genannte THIS-Scope bildet den öffentlichen Variablenbereich einer CFC-Instanz. Hier platzierte Variablen sind über direkte Aufrufe erreichbar und somit im Sinne einer objektorientierten Kapselung nicht geschützt. Variablen, die im so genannten VARIABLES-Scope erzeugt werden, sind private Variablen der Instanz, auf sie kann nur innerhalb der CFC zugegriffen werden:
...
...
Der Versuch der Ausgabe der Inhalte beider Datenfelder innerhalb eines CFOUTPUT-Tags funktioniert für die Variable im THIS-Scope problemlos, der Zugriff auf die Variable im VARIABLES-Scope wird von Cold Fusion MX mit einer Fehlermeldung beantwortet:
#myCFCInstance.name#
#myCFCInstance.firstname#
Lassen Sie sich nun nicht vom Tenor der Fehlermeldung stören, der den Eindruck vermittelt, dass die Variable firstname nicht vorhanden sei – obwohl sie doch offensichtlich in der CFC deklariert wurde. Cold Fusion hat aus eigener Warte Recht: Die Variable firstname ist in der gewünschten Sicht auf die CFC nicht vorhanden, da sie im privaten Scope verborgen ist.
Konstruktoren
Cold Fusion MX & Objekte
Konstruktoren enthalten Code, der beim Erzeugen einer Instanz einer Cold Fusion Component einmalig ausgeführt wird. In Macromedias CFC-Framework ist das Konstruktor-Konzept leider nur halbherzig umgesetzt. Praktisch bedeutet das, dass jeglicher Code außerhalb eines CFFUNCTION-Tag-Paars als Konstruktor-Code angesehen wird, der automatisch und einmalig mit dem Erzeugen einer CFC-Instanz von oben nach unten abgearbeitet wird:
...
...
Dieses Verhalten ist zwar funktional, aber weit entfernt von dem, was gemeinhin als gutes Software-Design angesehen ist. Ein besserer Weg ist die Verlagerung des Konstruktor-Codes in eine separate Methode init(), die beim Erzeugen der Instanz aufgerufen wird und sich selbst an die aufrufende Seite ausliefert:
Die Instanz der CFC ist nun in einer CFML-Seite oder einer weiteren CFC wie folgt zu erzeugen:
myCFCInstanz = CreateObject("component", "myCFC").init();
Diese Vorgehensweise erfordert allerdings ein wenig Disziplin und die strikte Vermeidung von Applikationscode außerhalb von CFFUNCTION-Tag-Paaren in Cold Fusion Components.
Anwendung: Warenkorb
Cold Fusion MX & Objekte
Als Anwendungsbeispiel einer zustandstragenden und damit Instance-based CFC dient ein einfacher Warenkorb. Dieser Warenkorb ist in Form einer CFC cart.cfc implementiert und stellt eine einfache interne Datenstruktur sowie Methoden zum Arbeiten mit dieser Datenstruktur bereit.
Instanzen der cart-CFC können in einem vollwertigen
E-Commerce-System an Benutzersessions angehängt werden und so als zustandstragende Objekte des Einkaufskorbs eines Users dienen.
Internes Datenmodell
Cold Fusion MX & Objekte
Das interne Datenmodell der CFC ist sehr einfach gehalten. Auf der obersten Ebene besteht es aus zwei Variablen im VARIABLES-Scope der CFC, die im Konstruktor deklariert und initialisiert werden.
Die Variable stCartData ist dabei als structure angelegt, die Variable nTotal hat den Typ numeric:
variables.stCartData = StructNew();
variables.nTotal = 0;
Der Wert des Parameters RETURNTYPE im Konstruktor spiegelt hierbei den CFC-Pfad ausgehend vom Cold-Fusion-Webroot zur Komponente wider. Im hier gezeigten Code-Stück wird also erwartet, dass die CFC cart.cfc in einer Ordnerstruktur /ipro/cart.cfc zu finden ist.
Anwendungslogik
Cold Fusion MX & Objekte
Da das CFC-Datenmodell in privaten Variablen angelegt ist, muss die CFC neben reiner Business-Logik auch Mittel und Wege bereitstellen, mit denen es ermöglicht wird, auf die Datenfelder zuzugreifen.
Zuerst werden Methoden zum Füllen, Aktualisieren und Löschen des Warenkorbs benötigt. Diese Aufgaben werden durch add(), update() und delete() erledigt.
All diese Methoden nutzen das Datenfeld stCartData und erzeugen in dieser Struktur eine Unterstruktur, deren Name der Artikelnummer des Produkts entspricht, das in den Warenkorb zu legen ist. In diesen Unterstrukturen werden dann Datenfelder für Preis, Anzahl und Zwischensumme des Preises erzeugt.
Ein detaillierter Blick auf die Methode add() erläutert das Konzept anhand verschiedener Code-Fragmente.
Zuerst wird die Funktion mit Hilfe des CFFUNCTION-Tags deklariert und als öffentliche Methode freigegeben. Der Wert no für das Attribut OUTPUT signalisiert, dass von dieser Methode keine direkte Ausgabe an den Browser erwartet wird. Mit Hilfe von drei Aufrufen des Tags CFARGUMENT werden Werte für Artikelnummer (sItem), Anzahl (nValue) und Preis (nPrice) an die Methode übergeben:
ACCESS="public" OUTPUT="no"
RETURNTYPE="boolean">
REQUIRED="yes" TYPE="string">
REQUIRED="yes" TYPE="numeric">
REQUIRED="yes" TYPE="numeric">
Das Erzeugen der Untereinträge in der stCartData-Struktur ist innerhalb eines CFSCRIPT-Blocks implementiert, da man hierfür sehr stark mit Strukturen, Cold-Fusion-Funktionen und Variablenzuweisungen hantiert. Diese Aufgaben sind in CFSCRIPT deutlich schneller und besser lesbar zu erledigen.
Zunächst wird eine Unterstruktur mit der Artikelnummer als eindeutigem Bezeichner angelegt:
StructInsert(variables.stCartData,
arguments.sItem, StructNew());
In dieser Unterstruktur legt StructInsert() nun Schlüssel-Wert-Paare mit Anzahl, Preis und Zwischensumme an. Dabei wird die Zwischensumme aus beiden vorherigen Werten berechnet. Zu beachten ist dabei, dass sItem, nValue und nPrice nur in einem besonderem Scope innerhalb der CFC-Methode verfügbar sind, dem so genannten ARGUMENTS-Scope:
StructInsert(Evaluate("variables.
stCartData.#arguments.sItem#"),
"times", arguments.nValue);
StructInsert(Evaluate("variables.
stCartData.#arguments.sItem#"),
"price", arguments.nPrice);
StructInsert(Evaluate("variables.
stCartData.#arguments.sItem#"),
"subtotal", arguments.nValue*
arguments.nPrice);
Die update()-Methode funktioniert prinzipiell analog zur add()-Methode ? man übergibt ihr einfach via CFARGUMENT die benötigten Parameter, die Verarbeitung der Parameter nutzt jedoch die Cold Fusion-Funktion StructUpdate().
Diese Funktion überschreibt gegebenenfalls die bereits vorhandenen Werte. Wenn die Struktur noch nicht vorhanden ist, erzeugt Cold Fusion eine Ausnahme, die entsprechend behandelt werden muss:
StructUpdate(Evaluate("variables.
stCartData.#arguments.sItem#"),
"times", arguments.nValue);
StructUpdate(Evaluate("variables.
stCartData.#arguments.sItem#"),
"price", arguments.nPrice);
...
bReturn = false;
In der vorliegenden Version ist die Fehlerbehandlung möglichst einfach gehalten und nur über das Setzen einer Variablen vom Typ boolean geregelt.
Einkapselung
Cold Fusion MX & Objekte
Um trotz der objektorientierten Datenkapselung in private Variablen Zugriff auf die Daten der CFC-Instanz zu haben, benötigen Sie neben den Methoden add(), update() und delete() zur Datenmanipulation noch die Methoden getCart() und getTotal() zum lesenden Zugriff auf die Datenfelder:
RETURNTYPE="struct">
Die Methode getTotal() arbeitet entsprechend und liefert nur die lokale, private Variable aus.
Weiterhin ist eine Methode calculateTotal() vorhanden, die über die in variablers.stCartData abgelegten Warenkorbdaten den Gesamtpreis des Warenkorbs berechnet und diesen im Datenfeld variables.nTotal ablegt.
Die Komponente ist nun so weit als Instance-based CFC nutzbar und kann aus nahezu jedem vorstellbaren Kontext in Cold Fusion aufgerufen und genutzt werden. Eine Instanz der CFC lässt sich generell über die Verwendung der Funktion CreateObject() erzeugen:
myCartInstance = CreateObject("component",
"ipro.cart").init();
Sie können die Instanz nun unter Verwendung der Methoden add(), update() sowie delete() mit Daten bestücken. Ein Aufruf von calculateTotal() berechnet den Gesamtwert des Warenkorbs:
myCartInstance.add("A1234", 12, 27.65);
myCartInstance.add("A1235", 4, 5454.5);
myCartInstance.add("A1236", 3, 23.23);
a.calculateTotal();
Die Methoden getCart() und getTotal() dienen dem Lesezugriff auf die Daten. Diese sind in das Tag CFDUMP eingebettet, das hier zur Ausgabe der Daten genutzt wird. Es ist klar, dass diese Form der Ausgabe und der Darstellung nicht den Ansprüchen an ein vollwertiges Shop-System genügt. Sie demonstriert aber sehr deutlich die verschachtelte Struktur des Datenfelds stCartData.
Schalten Sie im Cold-Fusion-Administrator das Debugging von CFML-Seiten an, so finden Sie in der Debug-Ausgabe eine Liste der Abarbeitung der einzelnen CFC-Aufrufe. Diese Ausgabe ist sehr hilfreich, um die Performance des CFC-Handlings zu überprüfen und gegebenenfalls kritische Stellen zu finden.
Ausblick und weitere Optionen
Cold Fusion MX & Objekte
Ein weiterer Schritt besteht nun darin, die CFC-Instanz in einem persistenten Scope zu platzieren, damit sie über den Aufruf einer CFM-Seite hinaus für den Benutzer verfügbar ist. Diese Aufgabe ist in Cold Fusion vergleichsweise einfach lösbar, da sich Session-Variablen sehr einfach über die Verwendung des SESSION-Scopes erzeugen lassen:
session.myCartInstance = CreateObject("component",
"ipro.cart").init();
Dieser Code erzeugt die Instanz der Cart-CFC im Session-Scope und macht diese damit für den Zugriff von jeder der Applikation zugeordneten CFM-Seite verfügbar.
Fazit
Cold Fusion MX & Objekte
Cold Fusion unterstützt verschiedene objektorientierte Mechanismen wie Datenkapselung und Konstruktoren. Die zustandstragenden CFCs entsprechen Objektinstanzen und lassen sich einfach in die Session-Kontexte von Anwendungen überführen.