Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Verschachtelte Records (https://www.delphipraxis.net/196125-verschachtelte-records.html)

Shadowwalker 24. Apr 2018 21:05

Verschachtelte Records
 
Um Programmoptionen strukturiert irgendwo ablegen zu können statt in globalen Variablen dachte ich an Records in Art

Delphi-Quellcode:
type
 TProgrammEinstellungen = record
 public
  type Allgemein = record
   class var Bla...
  end;

  type Dateien = record
   class var Bla...
  end;
 end;
Lesend und schreibend etwa so erreichbar
Delphi-Quellcode:
 TProgrammEinstellungen.Allgemein.Bla :=

 TProgrammEinstellungen.Dateien.Bla :=
So wäre das ein bisschen geordnet und man hat die Variablen thematisch noch einmal gruppiert.
Macht man das so oder doch ganz anders?

scrat1979 24. Apr 2018 21:17

AW: Verschachtelte Records
 
Ich habe mir dafür eine „richtige“ Klasse TSettings erstellt. Dann einfach

Delphi-Quellcode:
Settings := TSettings.Create;
Settings.LoadFromFile(...);
[...]
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..

Warum Records und keine Klasse?

Shadowwalker 24. Apr 2018 21:21

AW: Verschachtelte Records
 
Weil mir die Autovervollständigung sonst in der Liste zu viele Dinge anzeigt, die ich selber nirgendwo deklariert habe.

Redeemer 24. Apr 2018 23:57

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.

freimatz 25. Apr 2018 09:27

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.

Shadowwalker 25. Apr 2018 10:22

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.

Blup 25. Apr 2018 11:56

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.

Shadowwalker 25. Apr 2018 12:06

AW: Verschachtelte Records
 
Versteh mich nicht falsch, aber könntest du das auch für jemanden erklären der nicht in dieser Materie drinsteckt?

Benedikt Magnus 25. Apr 2018 12:46

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;

Blup 25. Apr 2018 13:05

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 = 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;
TAnwendungsfallSummeConfig könnte im einfachsten Fall auch ein Record oder verschachtelter Record sein.
Wichtig ist aber die Trennung "Global" vom konkreten Anwendungsfall.

Anwendungsfall im Programm erzeugen
Delphi-Quellcode:
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;
Im Testprojekt gibt es die Unit 'Global' nicht. Auch das Formular wird dort nicht gebraucht.
Dort soll unter anderem die Berechnung für jeden Rundungstyp getestet werden.
Wie man eine Testanwendung und Testfälle erzeugt ist bekannt?

MichaelT 25. Apr 2018 13:06

AW: Verschachtelte Records
 
Die Antwort kam bereits von Benedikt Magnus

Delphi-Quellcode:

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.
Anwendung dafür ist mir bis jetzt noch keine eingefallen.


Zitat:

Zitat von Shadowwalker (Beitrag 1400398)
Um Programmoptionen strukturiert irgendwo ablegen zu können statt in globalen Variablen dachte ich an Records in Art

Delphi-Quellcode:
type
 TProgrammEinstellungen = record
 public
  type Allgemein = record
   class var Bla...
  end;

  type Dateien = record
   class var Bla...
  end;
 end;
Lesend und schreibend etwa so erreichbar
Delphi-Quellcode:
 TProgrammEinstellungen.Allgemein.Bla :=

 TProgrammEinstellungen.Dateien.Bla :=
So wäre das ein bisschen geordnet und man hat die Variablen thematisch noch einmal gruppiert.
Macht man das so oder doch ganz anders?


Shadowwalker 25. Apr 2018 13:14

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:

Anwendung dafür ist mir bis jetzt noch keine eingefallen.
Habe ich im Eingangspost und in #6 geschrieben.

Benedikt Magnus 25. Apr 2018 13:22

AW: Verschachtelte Records
 
In meinem Beispiel kannst du nur
Delphi-Quellcode:
ProgrammEinstellungen.Allgemein.Bla
aufrufen,
Delphi-Quellcode:
Allgemein.Bla
gibt es nicht. Das von dir in Beitrag 6 beschriebene Problem ist damit also nicht gegeben.

Shadowwalker 25. Apr 2018 13:43

AW: Verschachtelte Records
 
Aber auch nur mit dieser zusätzlichen Zeile
Delphi-Quellcode:
var
  ProgrammEinstellungen: TProgrammEinstellungen;
Was ist hiergegen einzuwenden?
Delphi-Quellcode:
type
 TProgrammEinstellungen = record
 public
  type Allgemein = record
   class var Bla...
  end;

  type Dateien = record
   class var Bla...
  end;
 end;
Alle meine Units mit Prozeduren und Funktionen sind mit solchen Records mit class-vars aufgebaut.

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;

MichaelT 25. Apr 2018 14:29

AW: Verschachtelte Records
 
Warum machst du nicht ein Singleton der Form.


Zitat:

Zitat von Shadowwalker (Beitrag 1400459)
#9

#11
das ist das was ich im Eingangsbeitrag schrieb. Nur eben mit class-Variablen, damit ich keine lokalen Variablen benötige.
Zitat:

Anwendung dafür ist mir bis jetzt noch keine eingefallen.
Habe ich im Eingangspost und in #6 geschrieben.


Shadowwalker 25. Apr 2018 14:33

AW: Verschachtelte Records
 
Weil ich das nicht verstehe und lieber bei einfachen Sachen bleibe.

freimatz 25. Apr 2018 14:53

AW: Verschachtelte Records
 
http://clean-code-developer.de/die-g...le_stupid_KISS :-D

hoika 25. Apr 2018 15:49

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?

Shadowwalker 25. Apr 2018 16:02

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.

MichaelT 25. Apr 2018 17:02

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:
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.
Billige Gedankenspende.

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:
procedure test();
var
  config_c_a: Integer;
  config : TConfig;
begin
  config_c_a:=TAppConfig.b.a;
  GetModifyableConfig(config);

//  SetTAppConfig(TConfig);
end;
Astrein ist das nicht. Von so etwas halt ich wenig bis gar nichts. Einfach :-D naja.

Zitat:

Zitat von Shadowwalker (Beitrag 1400473)
Weil ich das nicht verstehe und lieber bei einfachen Sachen bleibe.


scrat1979 25. Apr 2018 22:00

AW: Verschachtelte Records
 
Zitat:

Zitat von hoika (Beitrag 1400487)
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?

Das ist soweit korrekt bzw. die Routinen müssen natürlich bei einer Änderung der Klasse angepasst werden. Da die streams bei mir aber NUR zum versenden über das Netzwerk (also temporär) verwendet werden ist das nicht so wild. Da in diesem Fall der Programmeinstellungen meine Variablen mit Standardwerten initialisiert werden wäre es nicht so tragisch wenn durch „LoadFromStream“ nach einer Erweiterung der Klasse diese Variable nicht „berücksichtigt“ würde. Die Streams sind ja nicht zwingend zu implementieren. Als permanenten Speicher verwende ich eine ganz normale INI-Datei, KÖNNTE aber auch einen Stream nehmen :)


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:14 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz