![]() |
Verschachtelte Records
Um Programmoptionen strukturiert irgendwo ablegen zu können statt in globalen Variablen dachte ich an Records in Art
Delphi-Quellcode:
Lesend und schreibend etwa so erreichbar
type
TProgrammEinstellungen = record public type Allgemein = record class var Bla... end; type Dateien = record class var Bla... end; end;
Delphi-Quellcode:
So wäre das ein bisschen geordnet und man hat die Variablen thematisch noch einmal gruppiert.
TProgrammEinstellungen.Allgemein.Bla :=
TProgrammEinstellungen.Dateien.Bla := Macht man das so oder doch ganz anders? |
AW: Verschachtelte Records
Ich habe mir dafür eine „richtige“ Klasse TSettings erstellt. Dann einfach
Delphi-Quellcode:
Lässt sich einfach erweitern und alles ist schön gekapselt. Da ich diese übers Netzwerk als Stream versende habe ich ihr noch die entsprechenden LoadFromStream und SaveToStream Methoden spendiert..
Settings := TSettings.Create;
Settings.LoadFromFile(...); [...] Warum Records und keine Klasse? |
AW: Verschachtelte Records
Weil mir die Autovervollständigung sonst in der Liste zu viele Dinge anzeigt, die ich selber nirgendwo deklariert habe.
|
AW: Verschachtelte Records
Was soll class var bei einem Record sein?
Anyway. Records werden nicht ineinander veraschachtelt deklariert sondern untereinander. Also erst Anwendung, Dateien und dann Programmeinstellungen. |
AW: Verschachtelte Records
Ich denke die ganze Diskussion könnte und sollte in mehrere Aspekte aufgeteilt werden:
1. Record statt einzelne Variablen 2. class var statt global 3. Klasse statt record Zu 1.: Statt einzelnen globalen Variablen finde ich es schon besser diese in einen Records zusammenzufassen. Zu 2.: eine class var ist quasi wie eine globale Variable. Sie ist immer da und wird nicht wie eine Objekt jeweils instanziiert. Da würde ich doch eine globale Variable vom typ des records besser finden. Es ist ehrlicher und nicht so versteckt. Vorteil ist auch noch, dass man die Variable auch noch kürzer benennen kann und den Typ schön lang. Bei mir heißt einer dieser Variablen schlicht "G". Zu 3.: ich wüsste jetzt nicht was eine Klasse da bringen sollte. Größere Logik sollte die eh nicht enthalten, kleinere Methoden kann man auch in records machen. Nachteil ist, dass man die Klasse dann instanzieren muss. |
AW: Verschachtelte Records
Hat es einen speziellen Grund, weshalb man Records nicht verschachteln darf?
Die Verschachtelung wollte ich machen, da ich dann sowas aufrufen kann TProgrammEinstellungen.Allgemein.Bla. Ohne Verschachtelung könnte ich auch das hier aufrufen Allgemein.Bla und ich wäre nicht gezwungen, TProgrammEinstellungen vor alles zu schreiben. |
AW: Verschachtelte Records
Die einzelnen Anwendungsfälle (bzw. deren Kern) sollten niemals direkt auf irgendwelche Sachen außerhalb zugreifen.
Erst recht nicht auf globale Variablen oder ähnliches. Sonst ist der Anwendungfall nicht mehr unabhängig testbar. Anwendungskern <-> Repository <-> globale Variable Das Repository könnte eine Klasse sein, die eine Kopie der für den Anwendungsfall wichigen globalen Einstellungen erhält. Alternativ wäre auch ein Interface denbar, das eine Klasse verbirgt, die Zugriffe auf globale Variablen weiter leitet. Im Testfall könnte der Testfall selbst dieses Interface bereitstellen. So kann man für globale Einstellungen einfach TSettings verwenden, aber auch die für den jeweiligen Anwendungsfall wichtigen Einstellungen über das Repository per Property direkt ansprechen. |
AW: Verschachtelte Records
Versteh mich nicht falsch, aber könntest du das auch für jemanden erklären der nicht in dieser Materie drinsteckt?
|
AW: Verschachtelte Records
Um die Ursprungsfrage zu beantworten und Redeemers Antwort etwas auszuführen, man kann Records problemlos verschachteln, nur findet die Deklaration der Typen getrennt von der verschachtelten Struktur statt.
Also so:
Delphi-Quellcode:
type
TAllgemein = record var Bla... end; TDateien = record var Bla... end; TProgrammEinstellungen = record Allgemein: TAllgemein; Dateien: TDateien; end; var ProgrammEinstellungen: TProgrammEinstellungen; |
AW: Verschachtelte Records
Die stark vereinfachte Skizze einer Beispiel-Anwendung:
Berechne die Summe von zwei Zahlen. Das gerundete Ergebnis wird ausgegeben. Wie das Ergebnis gerundet wird, legt eine globale Einstellung fest. Die Globalen Einstellungen werden vom Anwendungsfall nicht verändert.
Delphi-Quellcode:
TAnwendungsfallSummeConfig könnte im einfachsten Fall auch ein Record oder verschachtelter Record sein.
TAnwendungsfallSummeConfig = class()
puplic property Rundungstyp: Integer; end; TAnwendungsfallSumme = class() constructor Create(AConfig: TAnwendungsfallSummeConfig); destructor Destroy; override; {...} protected property Config: TAnwendungsfallSummeConfig; puplic property Wert1: Float; property Wert2: Float; property Ergebnis: Float; end; implementation constructor TAnwendungsfallSumme.Create(AConfig: TAnwendungsfallSummeConfig); begin inherited; FConfig := AConfig; end; destructor TAnwendungsfallSumme.Destroy; begin FConfig.Free; inherited; end; Wichtig ist aber die Trennung "Global" vom konkreten Anwendungsfall. Anwendungsfall im Programm erzeugen
Delphi-Quellcode:
Im Testprojekt gibt es die Unit 'Global' nicht. Auch das Formular wird dort nicht gebraucht.
var
config: TAnwendungsfallSummeConfig; begin config := TAnwendungsfallSummeConfig.Create; config.Rundungstyp := Global.Settings['Allgemein.Rundungstyp']; AnwendungsfallSumme := TAnwendungsfallSumme.Create(config); // wie man den Anwendungsfall mit dem Formular verknüpft, ist schon das nächste Thema ... MVC usw. end; Dort soll unter anderem die Berechnung für jeden Rundungstyp getestet werden. Wie man eine Testanwendung und Testfälle erzeugt ist bekannt? |
AW: Verschachtelte Records
Die Antwort kam bereits von Benedikt Magnus
Delphi-Quellcode:
Anwendung dafür ist mir bis jetzt noch keine eingefallen.unit recs; interface type TA = record type TB = record a : Integer; b : string; end; type TC = record a: Integer; b : string; c : string; end; end; implementation procedure test; var b: TA.TB; begin b.a:=1; end; end. Zitat:
|
AW: Verschachtelte Records
#9
dann habe ich aber das Problem was ich in #6 angesprochen habe #10 Klassen möchte ich keine verwenden. Records mit Class-Variablen bedeuten weniger Stress, weniger Fehleranfälligkeit wenn man mal irgendwas vergisst und weniger Code. #11 das ist das was ich im Eingangsbeitrag schrieb. Nur eben mit class-Variablen, damit ich keine lokalen Variablen benötige. Zitat:
|
AW: Verschachtelte Records
In meinem Beispiel kannst du nur
Delphi-Quellcode:
aufrufen,
ProgrammEinstellungen.Allgemein.Bla
Delphi-Quellcode:
gibt es nicht. Das von dir in Beitrag 6 beschriebene Problem ist damit also nicht gegeben.
Allgemein.Bla
|
AW: Verschachtelte Records
Aber auch nur mit dieser zusätzlichen Zeile
Delphi-Quellcode:
Was ist hiergegen einzuwenden?
var
ProgrammEinstellungen: TProgrammEinstellungen;
Delphi-Quellcode:
Alle meine Units mit Prozeduren und Funktionen sind mit solchen Records mit class-vars aufgebaut.
type
TProgrammEinstellungen = record public type Allgemein = record class var Bla... end; type Dateien = record class var Bla... end; end; Was absolut für eine Klasse mit Settern und Gettern spricht ist aber, dass ich schon in der Unit selber Überprüfungen durchführen kann hier mein erster eigener versuch
Delphi-Quellcode:
// Unit uSettings
uses uSettingsAllgemein; type TSettings = class private protected public Allgemein: TAllgemein; constructor Create; overload; destructor Destroy; override; end; var Settings: TSettings; implementation constructor TSettings.Create; begin inherited; Settings.Allgemein:= TAllgemein.Create; end; destructor TSettings.Destroy; begin Settings.Allgemein.Free; inherited; end;
Delphi-Quellcode:
// Unit uSettingsAllgemein
type TAllgemein = class private function GetBla: Integer; procedure SetBla(const Value: Integer); public constructor Create; overload; destructor Destroy; override; property Bla: Integer read GetBla write SetBla; end; implementation ... contructor destructor function TAllgemein.GetBla: Integer; begin Result .. keine Ahnung was hier hin muss end; procedure TAllgemein.SetBla(const Value: Integer); begin auch hier keine Ahnung wo was zugewiesen wird. hier würde ich aber prüfen, ob Value < -1 oder > X wäre end; |
AW: Verschachtelte Records
Warum machst du nicht ein Singleton
![]() Zitat:
|
AW: Verschachtelte Records
Weil ich das nicht verstehe und lieber bei einfachen Sachen bleibe.
|
AW: Verschachtelte Records
|
AW: Verschachtelte Records
Hallo,
Records können neuerdings auch Getter und Setter haben. Ich benutze aber auch immer Klassen dafür. Streams finde ich dagegen nicht so gut, weil, wenn die Klasse erweitert wird, funktioniert doch der ursprüngliche Stream nicht mehr. Oder bin da auf dem Holzweg? |
AW: Verschachtelte Records
Habe es nun ein bisschen unorthodox gemacht.
Eine Mischung von allem irgendwie aber trotzdem sauber. Meine Unit mit TSettings ist quasi ein Wrapper mit nur einer einzigen Prozedur Load. Load befindet sich in einer anderen Unit und diese ruft zum Schluss weitere Units auf. TAllgemein.LadeDies; TAllgemein.LadeDas; TDateien.LadeDies; TDateien.LadeDas. Die liste ist circa 75 Zeilen lang. Jede Unterkategorie der Programmeinstellungen hat eine eigene Unit wo schön sauber Setter und Getter drin sind. |
AW: Verschachtelte Records
Ein Verbundtyp fasst immer Variablen zusammen.
Entweder deklarierst eine Modulvariable im Interface und verwendest ...
Delphi-Quellcode:
unit recs_intf;
interface type TConfigRec = record type TB = record a : Integer; b : string; end; type TC = record a: Integer; b : string; c : string; end; public c : TC; b : TB; end; implementation end. und/oder versteckst die Konfiguration so halb
Delphi-Quellcode:
Billige Gedankenspende.
unit recs;
interface uses recs_intf; type TConfig = record conf : TConfigRec; private allowModify : Boolean; end; // Frei gestaltbar procedure GetModifyableConfig(var aTConfig: TConfig); procedure ModifyConfig(aTConfig : TConfig); //function ReadConfig : TConfig; function TAppConfig : TConfigRec; implementation var IntConfig : TConfigRec; function ReadConfig : TConfig; begin Result.conf:=IntConfig; Result.allowModify:=False; end; procedure GetModifyableConfig(var aTConfig: TConfig); begin aTConfig.conf:=IntConfig; aTConfig.allowModify:=True; end; procedure ModifyConfig(aTConfig : TConfig); begin if aTConfig.allowModify then IntConfig:= aTConfig.conf; end; // Die Funktion kann man noch rausziehen in ein eigenes Modul function TAppConfig : TConfigRec; begin Result := ReadConfig.conf; end; initialization end. Es gibt keine Forward Deklaration für Records. Damit kann man kein ordentlichen Abstarkten Datentyp machen. Dafür kannst du dann zumindest lesend auf die Config zugreifen.
Delphi-Quellcode:
Astrein ist das nicht. Von so etwas halt ich wenig bis gar nichts. Einfach :-D naja.
procedure test();
var config_c_a: Integer; config : TConfig; begin config_c_a:=TAppConfig.b.a; GetModifyableConfig(config); // SetTAppConfig(TConfig); end; Zitat:
|
AW: Verschachtelte Records
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:01 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz