Lade...
 

Wrapped Objects

Klassen CX_DESCRIPTIVE_REF, CX_OVERWRITING_REF

Im Folgenden werden die Klassen CX_DESCRIPTIVE_REF und deren Spezialisierung CX_OVERWRITING_REF beschrieben, welche in der ClassiX-Modellierung genutzt werden, um das Wrapper-Pattern (auch bekannt als Decorator-Pattern) umzusetzen.

Bisweilen ist die Tatsache, dass ein Objekt O1 ein anderes Objekt Oref referenziert, mit einer Bedeutung belegt, die sich von der Bedeutung der Referenz eines weiteren Objektes O2 auf Oref unterscheidet. Man könnte auch sagen: Objekt Oref wird von O1 aus unter einem anderen Aspekt gesehen als von O2 aus. Um dies zu modellieren muss eine die Referenz beschreibende Information entweder in den Objekten O1, O2 untergebracht werden oder man führt ein Zwischenobjekt (D1, D2) ein. Die letztere Variante bietet mehr Flexibilität und wird hier benutzt:

Objekt O1 verweist nicht direkt auf Oref sondern auf ein Zwischenobjekt D1, das beliebige Informationen über die Beziehung halten kann (als dynamische Attribute). Aus der Sicht von InstantView® ist dieses Zwischenobjekt der Klasse CX_DESCRIPTIVE_REF transparent: es besitzt auch alle Attribute und Methoden des eigentlich referenzierten Objekts:

a1 ... an sind Datenattribute, d1 ... dm sind die Attribute, mit denen die Relation charakterisiert wird, _object ist ein Pointer auf das eigentliche Zielobjekt der Relation.

Der Aspektgedanke wird weiter verallgemeinert, wenn das Zwischenobjekt auch Attribute des referenzierten Objekts überschreiben kann:

a1 ... ak ... an sind Datenattribute, d1 ... dm sind die Attribute, mit denen die Relation charakterisiert wird, ak wird durch a'k  überschrieben, _object ist ein Pointer auf das eigentliche Zielobjekt der Relation. Wenn man von einem bestimmten Objekt zum Objekt Oref navigiert, hat dieses (scheinbar) das Attribut ak'.

Mit Objekten der Klasse CX_OVERWRITING_REF können vollständige Zugriffsausdrücke überschrieben werden. Dabei kann es im Prinzip zu Widersprüchen kommen, die durch folgende Regeln aufgelöst werden:

  • Daten werden nur dann in das CX_OVERWRITING_REF-Objekt geschrieben, wenn sie sich von den Daten im Originalobjekt unterscheiden.
    Neben elementaren Daten und Objekten als Datenfeld können auch Pointer überschrieben werden, jedoch keine Collections.
  • Einmal überschriebene Daten bleiben immer überschrieben, auch wenn sie später einmal einen Wert annehmen, der mit dem des Originalobjekts übereinstimmt.
  • Überschriebene Daten können explizit mit Anweisung Unwrap wieder entfernt werden, d.h. man sieht durch das CX_OVERWRITING_REF-Objekt hindurch wieder auf die Originaldaten.
  • Sobald die mit expression1 erreichbaren Daten überschrieben wurden, kann man mit expression1.expression2 nichts mehr überschreiben. Andernfalls könnte man ja von den mit expression1 überschriebenen Daten nicht weiter navigieren. 
  • Außerdem werden immer bestimmte Teilausdrücke (Terme) am Ende eines Zugriffsausdrucks abgespalten, d.h. bei expression.Term würde nur expression überschrieben:
    Term sinnvoll für
    Unit() CX_VALUE
    denominator CX_VALUE_PER, CX_PERCENT_PER
    denominator.Unit()       CX_VALUE_PER
    Precision()               CX_NUMERIC, CX_PERCENT, CX_VALUE, ...
    [n]
    Integer (n-tes Bit), CXB_MULTIPLE_STRING (n-te Sprache), Collection (n-tes Element)
  • Sobald Daten mit expression1.expression2 überschrieben wurden, kann man nicht mehr mit expression1 schreiben. Lesender Zugriff mit expression1 ist natürlich möglich und würde auf das Originalobjekt verweisen.
    Wenn man expression1 überschreiben könnte, wäre es nicht mehr möglich, expression2 von expression1 zu erreichen. Genau das hatte man ursprünglich aber nicht beabsichtigt, denn man wollte ja nur das Original überdecken.
  • Es ist auch möglich, Daten zu überschreiben, die im Originalobjekt (noch) nicht existieren. Das ClassiX®-System kann bei komplexen Zugriffsausdrücken, die nicht mit einem dynamischen Datenfeld enden, nicht immer den Datentyp ermitteln. In diesem Fall erscheint eine Fehlermeldung.
  • Für Collections ist der Modus "überschrieben" nicht definiert. Eine Collection des Original-Objekts ist immer sichtbar. Es ist auch möglich, eine Collection im CX_OVERWRITING_REF-Objekt zu erzeugen, wenn das Original-Objekt keinen Collection mit diesem Namen besitzt und es sich um ein dynamisches Datenfeld handelt.

Was passiert beim Auswerten eines Zugriffsausdrucks?

  • Der Zugriffsausdruck wird mit dem Wrapper als Ursprung ausgewertet. Überschriebene Zugriffsausdrücke liefern an dieser Stelle bereits ein Ergebnis zurück. Der Wrapper wertet den Zugriffsausdruck also "ins Blaue" aus, er prüft vorher nicht, ob es überhaupt einen solchen überschriebenen Zugriffsausdruck gibt.
  • Bringt die Auswertung kein Ergebnis (auch: INVALID, NULL), wird der Zugriffsausdruck mit dem gewrappten Objekt als Ursprung ausgewertet. Das muss noch nicht zwingend das Core-Objekt sein, denn ein Objekt kann mehrfach gewrappt sein! Dieser Schritt wird so lange wiederholt, bis der Wrapper beim Core-Objekt angekommen ist.
  • Das Ergebnis ist das Endergebnis. Es kann auch NULL oder INVALID sein.
  • Vorsicht ist geboten, wenn der Zugriffsausdruck ein Makro ist. Sollte das Makro INVALID oder NULL als Ergebnis liefern, wird das Makro anschließend wiederholt aufgerufen, diesmal mit dem gewrappten Objekt als Ursprung.
  • Beim Auswerten eines Zugriffsausdrucks werden Zugriffsrechte beachtet. Der Wrapper verhält sich bezüglich der Zugriffsrechte genau wie das gewrappte Objekt. Dies gilt sowohl für OVERWRITE, als auch DESCRIPTIVE und unabhängig davon ob ein festes Datenfeld oder ein Slot angesprochen wird.

CX_DESCRIPTIVE_REF bzw. CX_OVERWRITING_REF umhüllen ein anderes Objekt (bilden einen "Wrapper"). Für diese Operation existieren die InstantView®-Anweisungen: WrapObject, RewrapObject, UnwrapObject und Unwrap.