Session D-POBJPersistente Objekte - Realisation
|
Spaltenname | Spaltentyp | Länge | Nachkomma | Null zulässig? | Validation-Trigger-Name |
---|---|---|---|---|---|
Init |
L |
1 |
0 |
Ja |
InitValidTrigger() |
Load |
L |
1 |
0 |
Ja |
LoadValidTrigger() |
Activate |
L |
1 |
0 |
Ja |
ActivateValidTrigger() |
GotFocus |
L |
1 |
0 |
Ja |
GotFocusValidTrigger() |
in dem dazugehörigen DBC wären die Eventroutinen in den Stored Procedures in ihrer absoluten Basisform codiert abgespeichert.
Durch Einfügen einer neuen Tabellenzeile (die Feldinhalte setze ich dabei jetzt erst einmal alle auf TRUE) "instanziere" ich eine Art "Laufzeitversion" dieser Basisklasse. Noch definiert sich die Klasse nur durch ihre vier Startup-Events, aber diese feuern wenigstens schon mal in der gewünschten Reihenfolge, nämlich Init, Load, Activate und zum Schluß GotFocus.
Nun möchte ich aber (genau wie in Visual FoxPro) das Standardverhalten durch eigenen Code ändern. Also ergänze ich die Tabellenstruktur wie folgt:
Spaltenname | Spaltentyp | Länge | Nachkomma | Null zulässig? | Validation-Trigger-Name |
---|---|---|---|---|---|
Init |
L |
1 |
0 |
Ja |
InitValidTrigger() |
_Init |
M |
1 |
0 |
Ja |
- keiner - |
Load |
L |
1 |
0 |
Ja |
LoadValidTrigger() |
_Load |
M |
1 |
0 |
Ja |
- keiner - |
Activate |
L |
1 |
0 |
Ja |
ActivateValidTrigger() |
_Activate |
M |
1 |
0 |
Ja |
- keiner - |
GotFocus |
L |
1 |
0 |
Ja |
GotFocusValidTrigger() |
_GotFocus |
M |
1 |
0 |
Ja |
- keiner - |
und in meinen dazugehörigen DBC würde ich die Eventroutinen in den Stored Procedures dahingehend erweitern, daß z.B. ein Codeblock-Interpreter den Sourcecode aus dem korrespondierenden Memofeld abarbeiten würde, falls es nicht leer ist.
Im nächsten Schritt würde ich mir noch ein eventuell fälliges Parameterübergabeverfahren ausdenken. Z.B. dürften meinem Init-Event Parameter übergeben werden, also ändern wir kurzerhand die Struktur:
Spaltenname | Spaltentyp | Länge | Nachkomma | Null zulässig? | Validation-Trigger-Name |
---|---|---|---|---|---|
Init |
C |
60 |
0 |
Ja |
InitValidTrigger() |
_Init |
M |
1 |
0 |
Ja |
- keiner - |
In das 60 Zeichen lange Feld können nun Variablennamen etc. eingetragen werden, die dann intern eben als Zeiger auf die Parameterwerte ausgewertet werden müssen.
Jetzt kommt die Realisation von Properties. Dies ist sehr einfach. Ich lege 2 Standard-Properties fest:
Spaltenname | Spaltentyp | Länge, | Nach-komma | Null zulässig? | Validation-Trigger-Name |
---|---|---|---|---|---|
Init |
C |
60 |
0 |
Ja |
InitValidTrigger() |
_Init |
M |
1 |
0 |
Ja |
- keiner - |
Load |
L |
1 |
0 |
Ja |
LoadValidTrigger() |
_Load |
M |
1 |
0 |
Ja |
- keiner - |
Activate |
L |
1 |
0 |
Ja |
ActivateValidTrigger() |
_Activate |
M |
1 |
0 |
Ja |
- keiner - |
GotFocus |
L |
1 |
0 |
Ja |
GotFocusValidTrigger() |
_GotFocus |
M |
1 |
0 |
Ja |
- keiner - |
Visible |
L |
1 |
0 |
Nein |
- keiner - |
Caption |
M |
4 |
0 |
Nein |
- keiner - |
Die Defaultwerte für diese zwei Properties erhalte ich wieder aus dem DBC (dort Feldgültigkeit Standardwert).
Und zum Schluß erfinde ich noch ein bis zwei weitere Methoden, die ich genau wie eingangs die Events einbaue....
Was kann man mit dieser Konstruktion jetzt machen? Nun, man kann sich seine Gedanken über solch ein Konzept machen:
Die gerade erzeugte Tabelle mitsamt dem dazugehörigen DBC stellt jetzt eigentlich WAS(?) dar, wenn man sie mal mit einer VCX, oder gar mit einer der VFP Basisklassen vergleicht?
Nun, man könnte behaupten, daß der DBC die Basisklasse darstellt. Besser (da präziser) formuliert müßte man sagen, daß der DBC die programmtechnische Realisation der PEM-Basisiklassenfunktionalität darstellt und darüber hinaus noch weitere allgemeingültige, objektorientierte Verhaltensmuster implementiert.
Die im DBC enthaltene Tabelle ist, um beim direkten Vergleich mit den VFP Basisklassen zu bleiben, der Platz, an dem die Laufzeitinstanz der Klasse angelegt wird - also so eine Art virtueller Arbeitsspeicher <g>.
Kann man den jetzt eigentlich so eine "Klasse" subclassen?? Eigentlich schon! Wir legen einfach eine neue Tabelle mit der gleichen Tabellenstruktur an. Dies ist nun unsere Subklasse. In Zukunft "APPEND BLANKen" wir dann nicht mehr einfach einen Satz um ein Objekt zu "instanziieren", sondern übertragen einfach die Zeile unserer Subklasse komplett in die "Arbeitsspeichersimulationstabelle"! In den Memofeldern unserer Subklasse haben wir dort unseren eigenen Code eingetragen, wo wir die Vererbung unterbrechen wollten. Das Übertragen leerer Memofelder feuert ja (wie bei Standardobjekten im Original) immer noch das im DBC abgelegte Standarverhalten.
Natürlich sind da einige "kleinere" Fallstricke drin, die man aber programmatisch umschiffen kann. So muß man alle Tabellen die in dieser Konstruktion als Speichertabelle, oder Subklassentabelle mitspielen immer synchron ändern, wenn man die Struktur erweitert, um z.B. eine Subklasse um bestimmte Events zu erweitern.
Natürlich geht es mir an dieser Stelle nicht darum die Speicherung und Instanziierung von VFP-Klassen neu zu erfinden. Lediglich die Idee der Abbildbarkeit eines instanziierten Speicherobjekts in den Adressraum von VFP's DBF-Tabellen ist hier erst einmal von Bedeutung. Auf dieser Grundüberlegung bauen die meisten der nachfolgenden Konzepte auf, die benutzt werden um die Implementation eines VFP-basierten Applikations-Worflow-Managment-Systems zu evaluieren.
So viel sei hier aber schon einmal verraten. Das Feuern von Events beim Einfügen von Tabellenzeilen, oder beim Ändern von Feldinhalten geht Visual FoxPro-intern brutal schnell! Und die Abarbeitung der zugeordneten, compilierten Stored Procedures im DBC ist auch ohne jeden Preformance-Hit - mit dem CodeBlock-Schneckenpost-Iterpreter nicht zu vergleichen!
Damit jetzt diese Technik allgemeingültig eingesetzt werden kann, muß ein Speicherungskonzept "erfunden" werden, das es uns ermöglicht, beliebig viele PEMs pro Objekt (variant) zu speichern. Dazu dient die ObjectStore Technik, die ich in meiner ersten Session bereits ausführlich vorgestellt habe.
Persistente Objekte können als Record-Sets in Tabellen eines DBC's abgebildet werden. Mit den entsprechenden Methoden und Trigger-Routinen läßt sich eine vorwärtsgerichtete Wiederherstellung des Speicherobjekts realisieren.