In der Ulmer Oberon-Bibliothek wird die Eigenschaft der Persistenz durch das Modul PersistentObjects definiert, das den Typ PersistentObjects.Object exportiert, der eine Erweiterung von Services.Object ist. Persistenz wurde als primäre Abstraktion definiert, da
Der für PersistentObjects notwendige Verwaltungsaufwand besteht analog zu Erweiterungen von Services.Object aus der Registrierung der Typen (Services.CreateType), der Verknüpfung von Objekten mit Typen (Services.Init) und zusätzlich den angesprochenen Schnittstellenprozeduren. Um den Verwaltungsaufwand zu reduzieren, wird die Registrierung bei Services von PersistentObjects übernommen.
Für einen einfachen Objektdatentyp für ganze Zahlen sieht das beispielsweise folgendermaßen aus:6.4
Gegeben sei der Datentyp:
TYPE Integer = POINTER TO IntegerRec; IntegerRec = RECORD (PersistentDisciplines.ObjectRec) val: INTEGER END;
Als Basistyp wird hier PersistentDisciplines.Object verwendet, da damit persistente Disziplinen unterstützt werden - genauso wie typischerweise Disciplines.Object gegenüber Objects.Object vorzuziehen ist. Dann sind folgende Änderungen im Vergleich zu einer direkten Erweiterung von Services.Object notwendig:
Services.CreateType(type, "Integers.Integer", "");
nun
PROCEDURE Init; VAR persif: PersistentObjects.Interface; BEGIN NEW(persif); persif.create := CreateInteger; persif.read := ReadInteger; persif.write := WriteInteger; persif.createAndRead := NIL; PersistentObjects.RegisterType(type, "Integers.Integer", "PersistentDisciplines.Object", persif); END Init;
aufzurufen.
Services.Init(new, type);
die Verknüpfung mit
PersistentObjects.Init(new, type);
hergestellt.
PROCEDURE CreateInteger(VAR object: PersistentObjects.Object); VAR integer: Integer; BEGIN Create(integer, 0); object := integer; END CreateInteger; PROCEDURE ReadInteger(s: Streams.Stream; object: PersistentObjects.Object) : BOOLEAN; BEGIN WITH object: Integer DO RETURN NetIO.ReadInteger(s, object.val) END; END ReadInteger; PROCEDURE WriteInteger(s: Streams.Stream; object: PersistentObjects.Object) : BOOLEAN; BEGIN WITH object: Integer DO RETURN NetIO.WriteInteger(s, object.val) END; END WriteInteger;
Das Modul NetIO unterstützt die Ein- und Ausgabe aller Basistypen von Oberon in einer (für jeden Stream) adaptierbaren Weise, mit der ggf. Inkompatibilitäten zwischen verschiedenen Hardware-Architekturen ausgeglichen werden können.
Der Verwaltungsaufwand läßt sich bei Abstraktionen weiter reduzieren, die keine eigenen Daten unterhalten, die zu sichern und zu restaurieren sind. Dies trifft beispielsweise für eine Version von Collections zu, die persistent ist. Hier kann ganz einfach bei der Schnittstelle NIL übergeben werden und damit auf die Implementierung der Schnittstellenprozeduren verzichtet werden:
PersistentObjects.RegisterType(type, "Collections.Collection", "PersistentDisciplines.Object", NIL);
Auf solche Weise vorbereitete Objekte können dann mit PersistentObjects.Write in eine Sequenz von Bytes (über einen Stream) verwandelt oder von einem Stream mit PersistentObjects.Read wieder restauriert werden:
ok := PersistentObjects.Write(s, collection);
Beim Einlesen empfiehlt es sich, etwas vorsichtig zu sein, da nicht sichergestellt ist, daß genau das vorgefunden wird, was einmal früher korrekt abgelegt worden ist. Wenn beispielsweise
VAR collection: Collections.Collection; (* ... *) ok := PersistentObjects.Read(s, collection);
aufgerufen wird und das eingelesene Objekt keine Kollektion ist, dann hat PersistentObjects.Read keine Gelegenheit, FALSE zurückzuliefern - stattdessen gibt es einen Laufzeitfehler: type guard failure.
Um dies zu vermeiden, gibt es zwei Möglichkeiten:
VAR collection: Collections.Collection; object: PersistentObjects.Object; (* ... *) IF PersistentObjects.Read(s, object) & (object IS Collections.Collection) THEN collection := object(Collections.Collection); (* ... *) END;
VAR collection: Collections.Collection; guard: Services.Type; (* ... *) Services.SeekType("Collections.Collection", guard); (* ... *) ok := PersistentObjects.GuardedRead(s, guard, collection);
Hier führt PersistentObjects selbst einen Typentest mit guard durch, bevor eine Zuweisung an collection stattfindet und liefert FALSE zurück, falls das eingelesene Objekt keine Erweiterung des guard ist.