134 lines
5.9 KiB
Markdown
134 lines
5.9 KiB
Markdown
-----
|
|
|
|
### Projektplan: Atome und das "Builder/Snapshot"-Pattern
|
|
|
|
* **Datum:** 09. Oktober 2025
|
|
* **Uhrzeit:** 16:07
|
|
|
|
#### Motivation
|
|
|
|
Die bisherige Diskussion hat zwei grundlegende Anforderungen an die Zustandsverwaltung herausgearbeitet:
|
|
|
|
1. **Allgemeine Sicherheit:** Für generische Datenstrukturen (Maps, Vektoren) bieten persistente Datenstrukturen durch ihre Immutabilität und lock-freie Lesbarkeit die höchste Sicherheit und Flexibilität bei konkurrierendem Zugriff.
|
|
2. **Spezialisierte Performance:** Für bestimmte Anwendungsfälle (z.B. große, append-only Time-Series) ist die Performance einer In-Place-Mutation überlegen, aber das Teilen des Zustands zwischen Threads erfordert eine sichere Synchronisation.
|
|
|
|
Die Motivation ist daher, ein **standardisiertes, generisches Muster** zu definieren, das die rohe Performance einer mutierbaren "Arbeitskopie" mit der Sicherheit von unveränderlichen, lesbaren "Snapshots" für die konkurrierende Analyse kombiniert.
|
|
|
|
#### Ziel
|
|
|
|
1. Etablierung des **"Builder/Snapshot"-Patterns** als idiomatisches Kernkonzept der Sprache für den Umgang mit hoch-performantem, geteiltem, mutablem Zustand.
|
|
2. Definition von **generischen Basis-Interfaces** (`IBuilder`, `IImmutable`), die dieses Pattern im Typensystem verankern.
|
|
3. Nahtlose Integration dieses Patterns mit den bestehenden `atom`- (für geteilten Zustand) und `var`- (für lokalen Zustand) Konstrukten.
|
|
|
|
#### Ergebnis
|
|
|
|
Das Ergebnis ist ein klares, zweigleisiges Modell für die Zustandsverwaltung, das auf dem generischen "Builder/Snapshot"-Pattern aufbaut.
|
|
|
|
**1. Das generische Pattern: `IBuilder` / `IImmutable`**
|
|
|
|
Wir definieren zwei konzeptionelle Basis-Interfaces, die den Vertrag des Patterns abbilden:
|
|
|
|
* `IImmutable`: Ein **Marker-Interface**, das einen sicheren, unveränderlichen "Produkt"- oder Snapshot-Typ kennzeichnet.
|
|
* `IBuilder`: Ein Interface, das einen "Builder" kennzeichnet. Es definiert die Fähigkeit, ein `IImmutable`-Produkt zu erzeugen.
|
|
|
|
**2. Die Spezialisierung (Beispiel: `Series`-Interfaces)**
|
|
|
|
Konkrete Datenstrukturen wie die `Series` spezialisieren diese generischen Rollen.
|
|
|
|
```delphi
|
|
// --- Generische Basis-Interfaces ---
|
|
|
|
// Kennzeichnet ein sicheres, unveränderliches Lese-Objekt (Snapshot).
|
|
IImmutable = interface(IInterface)
|
|
end;
|
|
|
|
// Kennzeichnet ein mutierbares Builder-Objekt, das Snapshots von sich erzeugen kann.
|
|
IBuilder = interface(IInterface)
|
|
function CreateSnapshot: IImmutable;
|
|
end;
|
|
|
|
// --- Spezialisierte Interfaces für die 'Series' ---
|
|
|
|
// ISeries ist die spezialisierte, lesbare Form von IImmutable.
|
|
ISeries = interface(IImmutable)
|
|
function GetCount: Int64;
|
|
function GetItems(Idx: Integer): TScalar;
|
|
property Count: Int64 read GetCount;
|
|
property Items[Idx: Integer]: TScalar read GetItems; default;
|
|
end;
|
|
|
|
// IWriteableSeries ist der spezialisierte Builder.
|
|
// Wichtig: Er implementiert den IBuilder-Vertrag, indem er eine CreateSnapshot-Methode
|
|
// anbietet, die den spezialisierten Snapshot-Typ ISeries zurückgibt.
|
|
IWriteableSeries = interface(IBuilder)
|
|
function GetCount: Int64;
|
|
property Count: Int64 read GetCount;
|
|
procedure Add(const Item: TScalar.TValue; Lookback: Int64 = -1);
|
|
function CreateSnapshot: ISeries; // Covarianter Return-Type zum Basis-Interface
|
|
end;
|
|
```
|
|
|
|
**3. Die Anwendungsbeispiele in der Sprache**
|
|
|
|
Das Pattern wird auf zwei Arten verwendet, je nach Kontext (`atom` oder `var`). Die RTL-Funktionen arbeiten mit den generischen `IBuilder`- und `IImmutable`-Konzepten.
|
|
|
|
**Beispiel 1: Geteilter Zustand (via `atom`)**
|
|
|
|
Der `atom` schützt den `IBuilder` vor konkurrierenden Schreibzugriffen.
|
|
|
|
```lisp
|
|
(*
|
|
* Szenario: Ein Ticker-Modul (Thread A) empfängt Live-Daten,
|
|
* während eine UI (Thread B) die Daten zur Analyse anfordert.
|
|
*)
|
|
|
|
; Ein globaler 'atom' wird erstellt. Er hält die einzige Instanz des Builders.
|
|
(atom live-ticker (create-series-builder))
|
|
|
|
; -- Thread A (Produzent) --
|
|
; Modifiziert den Builder sicher über eine generische, synchronisierte RTL-Funktion,
|
|
; die auf jedem IBuilder in einem Atom funktioniert.
|
|
(builder-update! live-ticker series-add new-tick-value)
|
|
|
|
; -- Thread B (Konsument) --
|
|
; Fordert einen sicheren, unveränderlichen Snapshot an.
|
|
; Die generische 'snapshot'-Funktion ruft intern 'CreateSnapshot' auf.
|
|
(let chart-data (snapshot live-ticker))
|
|
|
|
; 'chart-data' ist nun eine sichere ISeries (IImmutable), die ohne Locks gelesen werden kann.
|
|
(calculate-indicators chart-data)
|
|
```
|
|
|
|
**Beispiel 2: Lokaler Zustand (via `var`)**
|
|
|
|
Dies ist der "Maschinenraum"-Modus für maximale Single-Thread-Performance.
|
|
|
|
```lisp
|
|
(*
|
|
* Szenario: Innerhalb einer Funktion wird eine komplexe Datenreihe
|
|
* temporär aufgebaut, um ein einmaliges Ergebnis zu berechnen.
|
|
*)
|
|
(do
|
|
; Erzeuge einen lokalen, unsynchronisierten Builder.
|
|
(var local-builder (create-series-builder))
|
|
|
|
; Befülle den Builder direkt und ohne Lock-Overhead über typspezifische,
|
|
; unsynchronisierte RTL-Funktionen.
|
|
(repeat 1000000
|
|
(series-add! local-builder (get-next-value)))
|
|
|
|
; Erzeuge einen finalen Snapshot für die Weiterverarbeitung.
|
|
(let final-data (series-create-snapshot local-builder))
|
|
|
|
; Gib das Ergebnis der Analyse zurück.
|
|
(run-analysis-on final-data))
|
|
```
|
|
|
|
#### Todo
|
|
|
|
* [ ] Generische `IBuilder`- und `IImmutable`-Interfaces in Delphi definieren.
|
|
* [ ] Finale `ISeries`- und `IWriteableSeries`-Interfaces (als Spezialisierungen) implementieren.
|
|
* [ ] Eine konkrete `TSeries`-Klasse implementieren, die `IWriteableSeries` implementiert.
|
|
* [ ] Die generischen, synchronisierten RTL-Funktionen `(builder-update! ...)` und `(snapshot ...)` für `atom`s erstellen, die auf `IBuilder` operieren.
|
|
* [ ] Die spezifischen, unsynchronisierten RTL-Funktionen `(series-add! ...)` und `(series-create-snapshot ...)` für die `TSeries`-Implementierung erstellen.
|
|
* [ ] Unit-Tests für beide Anwendungsfälle schreiben. |