AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Verständnisproblem: Globale, gruppierte Konstanten
Thema durchsuchen
Ansicht
Themen-Optionen

Verständnisproblem: Globale, gruppierte Konstanten

Ein Thema von Keks · begonnen am 21. Jul 2014 · letzter Beitrag vom 23. Jul 2014
Antwort Antwort
Seite 2 von 2     12   
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#11

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 07:25
Hier ein ValueObject in Aktion:
Delphi-Quellcode:
program dp_181169;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  Tier in 'Tier.pas';

procedure Test;
var
  LTier : TTier;
begin
  for LTier in TTier._All do // <- alle Tiere anzeigen
    begin
      WriteLn( LTier.name );
    end;

  LTier := TTier.Create( 5 ); // <- erzeugen mit einer TierId
  try
    if LTier.Equals( TTier.Hund ) // <- vergleichen mit Hund
    then
      WriteLn( 'Wie ein/e ' + TTier.Hund.name );
    if LTier.Equals( TTier.Katze ) // <- vergleichen mit Katze
    then
      WriteLn( 'Wie ein/e ' + TTier.Katze.name );
    if LTier.Equals( TTier.Maus ) // <- vergleichen mit Maus
    then
      WriteLn( 'Wie ein/e ' + TTier.Maus.name );
  finally
    LTier.Free; // <- Instanz wird entfernt
  end;

  LTier := TTier.Hund; // <- Instanz über Klassen-Eigenschaft
  try
    WriteLn( 'Hier ist ein/e ', LTier.name );
  finally
    LTier.Free; // <- macht nichts
  end;

  WriteLn( 'Hier ist immer noch ein/e ', LTier.name ); // <- Kein Problem

  FreeAndNil( LTier ); // <- wird nur auf nil gesetzt

  LTier := TTier.Create( 0815 ); // <- Exception, weil ungültige TierId

end;

begin
  ReportMemoryLeaksOnShutdown := True;
  try
    Test;
  except
    on E : Exception do
      WriteLn( E.ClassName, ': ', E.Message );
  end;
  ReadLn;

end.
und die Ausgabe
Code:
Hund
Katze
Maus
Wie ein/e Maus
Hier ist ein/e Hund
Hier ist immer noch ein/e Hund
EArgumentOutOfRangeException: Ungültige TierId
Ein robustes ValueObject bedarf unter Delphi allerdings einiges an zusätzlichem Gebimmel-Bammel, da wir ja auf die lifetime der Instanzen achtgeben müssen.

Dafür kann aber auch der DAP (Dümmste Anzunehmende Programmierer) es niemals schaffen eine ungültige Instanz dieses ValueObjects zu erzeugen (solange er den Source dieser Klasse nicht anfasst).

Ob die einzelnen möglichen Werte direkt in der Klasse/im Quelltext hinterlegt werden hängt hierbei immer vom jeweiligen Kontext/Einsatzgebiet ab.

Wenn neue mögliche Werte hinzukommen und diese auch noch eine weitere Berücksichtigung in der Anwendung benötigen, dann baut man die Werte tatsächlich fest ein (Diese Anwendung kann halt nur mit Hund, Katze, Maus umgehen, aber nicht mit dem neu hinzugekommen Elefanten, dazu muss noch mehr angepasst werden).
Delphi-Quellcode:
unit Tier;

interface

uses
  System.Generics.Collections;

type
  TValueObject = class abstract
  public
    function SameValueAs( Other : TValueObject ) : Boolean; virtual; abstract;
    function Equals( Obj : TObject ) : Boolean; override;
  end;

  TTier = class( TValueObject )
{$REGION 'values'}
  private type
    TValue = record
      Id : Integer;
      Name : string;
    end;

  const
    _Values : array [0 .. 2] of TValue = ( ( Id : 0; name : 'Hund' ), ( Id : 12; name : 'Katze' ), ( Id : 5;
        name : 'Maus' ) );
    class procedure BuildItems;
{$ENDREGION}
{$REGION 'class'}
  private
    class var _Items : TList<TTier>;
    class var _ItemsDict : TDictionary<Integer, TTier>;
    class var _Shutdown : Boolean;
    class function GetTier( const Index : Integer ) : TTier; static;
    class function GetAll : TArray<TTier>; static;
  protected
    class constructor Create;
    class destructor Destroy;
  public
    class property _All : TArray<TTier> read GetAll;
    class property Hund : TTier index 0 read GetTier;
    class property Katze : TTier index 12 read GetTier;
    class property Maus : TTier index 5 read GetTier;
{$ENDREGION}
{$REGION 'instance'}
  private
    FId : Integer;
    FName : string;
    function SameTierAs( Other : TTier ) : Boolean;
    constructor CreateItem( );
{$ENDREGION}
  public
    constructor Create( TierId : Integer );
    destructor Destroy; override;

    function SameValueAs( Other : TValueObject ) : Boolean; override;
    function GetHashCode : Integer; override;
    function ToString : string; override;
    procedure FreeInstance; override;

    property Id : Integer read FId;
    property name : string read FName;

  end;

implementation

uses
  System.SysUtils;

{ TValueObject }

function TValueObject.Equals( Obj : TObject ) : Boolean;
begin
  Result := ( Self = Obj ) or Assigned( Obj ) and ( Self.ClassType = Obj.ClassType ) and
    SameValueAs( Obj as TValueObject );
end;

{ TTier }

class procedure TTier.BuildItems;
var
  LValue : TValue;
  LItem : TTier;
begin
  if Assigned( _ItemsDict )
  then
    Exit;

  _Items := TObjectList<TTier>.Create( True );
  _ItemsDict := TDictionary<Integer, TTier>.Create( );

  for LValue in _Values do
    begin
      LItem := Self.CreateItem;
      LItem.FId := LValue.Id;
      LItem.FName := LValue.Name;
      _Items.Add( LItem );
      _ItemsDict.Add( LValue.Id, LItem );
    end;
end;

constructor TTier.Create( TierId : Integer );
begin
  inherited Create;
  if not _ItemsDict.ContainsKey( TierId )
  then
    raise EArgumentOutOfRangeException.Create( 'Ungültige TierId' );
  FId := TierId;
  FName := _ItemsDict[TierId].Name;
end;

constructor TTier.CreateItem;
begin
  inherited;
end;

class constructor TTier.Create;
begin
  BuildItems;
end;

destructor TTier.Destroy;
begin
  if _Items.Contains( Self ) and not _Shutdown
  then
    Exit;
  inherited;
end;

procedure TTier.FreeInstance;
begin
  if _Items.Contains( Self ) and not _Shutdown
  then
    Exit;
  inherited;
end;

class function TTier.GetAll : TArray<TTier>;
begin
  Result := TTier._Items.ToArray;
end;

function TTier.GetHashCode : Integer;
begin
  Result := FId;
end;

class function TTier.GetTier( const Index : Integer ) : TTier;
begin
  Result := TTier._ItemsDict[index]
end;

class destructor TTier.Destroy;
begin
  TTier._Shutdown := True;
  TTier._ItemsDict.Free;
  TTier._Items.Free;
end;

function TTier.SameTierAs( Other : TTier ) : Boolean;
begin
  Result := Assigned( Other ) and ( Self.FId = Other.FId );
end;

function TTier.SameValueAs( Other : TValueObject ) : Boolean;
begin
  Result := ( Self = Other ) or Assigned( Other ) and ( Self.ClassType = Other.ClassType ) and
    SameTierAs( Other as TTier );
end;

function TTier.ToString : string;
begin
  Result := FName;
end;

end.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo (22. Jul 2014 um 07:30 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.136 Beiträge
 
Delphi 12 Athens
 
#12

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 08:07
Du kannst auch sowas machen:
TTier = (tiHund = 1, tiKatze = 27, tiMaus = 15, ...)
Hier sollte man aber wissen, daß die RTTI so nicht mehr funktioniert, da hierbei die NamensListe der einzelnen Werte nicht mehr gespeichert wird.

Man kann hier dafür den ordinalen Wert speichern und nahezu beliebig festlegen,
aber Namen nur noch über eine selbst definierte Liste. (nicht aus der RTTI auslesbar)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.060 Beiträge
 
Delphi 10.4 Sydney
 
#13

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 08:46
Uiuiui, da wird wieder mit den dicksten Kanonen auf kleinste Spatzen geschossen!

Die Idee von Nuclearping ist die simpelste und wahrscheinlich am schnellsten umsetzbare Lösung.

Ggf. kann man das noch per Pseudonamespaces und Klassen schöner verpacken:

Delphi-Quellcode:
  TLifeForms = class
  public type
    TAnimal = class
    public type
      TMammal = (Dog = 1, Cat = 27, Mouse = 15);
    end;

    TPlants = class
    public type
      TRoses = (DogRose = 23, RugosaRose = 58);
    end;
  end;
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#14

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 09:36
Uiuiui, da wird wieder mit den dicksten Kanonen auf kleinste Spatzen geschossen!

Die Idee von Nuclearping ist die simpelste und wahrscheinlich am schnellsten umsetzbare Lösung.
Schnell und simpel ja, allerdings hat man nicht die gesamte Vereinbarung (wie hier mit einem externen System) in einem Objekt gekapselt und auch das Erzeugen über eine Id ist umständlicher.

Hier wird auch ein sehr simples Beispiel angeführt. In der Realität sind diese ValueObjects aber wesentlich komplexer (z.B. Währung). Teilweise können diese Werte weggelassen werden, weil kein Pflichtfeld und sind dann einfach nil oder bekommen einen Dummy-Wert zugewiesen (TTier.KeinTier) oder es wird nicht übergeben und dann wird automatisch der Dummy-Wert zurückgegeben.

Es ist dann schön, wenn man ein Konzept hat, was alle diese Anforderungen erfüllen kann und die Handhabung immer gleich ist.

Auch kann man ValueObjects (egal welchen Typs) einfach in eine Liste (z.B. Tags) packen.
Delphi-Quellcode:
TSomeObject = class
private
  FTags : TList<TValueObject>;
public
  procedure AddTag( ATag : TValueObject );
end;

SomeObject.Add( TTier.Create(5) );
SomeObject.Add( TColorInfo.Create( clRed ) );
...
Das die Klasse aufwändig ist und nicht zu den schnell mal eben hingetippten zählt ist mir durchaus bewusst, allerdings auch die Flexibilität und die Abgeschlossenheit in sich und ich kann mich jederzeit darauf verlassen, dass nur valide Werte ankommen.

Es hängt vom Einsatzgebiet und den zu erwartenden Erweiterungen ab ob ich so etwas ins Rennen bringe, je komplexer das System umso eher nehme ich solche Konstrukte.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo (22. Jul 2014 um 09:38 Uhr)
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.060 Beiträge
 
Delphi 10.4 Sydney
 
#15

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 11:15
Hier wird auch ein sehr simples Beispiel angeführt.

Es hängt vom Einsatzgebiet und den zu erwartenden Erweiterungen ab ob ich so etwas ins Rennen bringe, je komplexer das System umso eher nehme ich solche Konstrukte.
Also nichts gegen deine Lösung, die finde ich sehr gut und interessant.
Sie hat bei komplexen Systemen ihre Daseinsberechtigung.

Aber hier haben wir es (bisher) mit ein paar Enum-Werten zu tun, die in eine Ini-Datei gespeichert werden sollen.
Wie die eigentlichen Umstände in Keks' Programm sind, wissen wir nicht.

Von daher ist das YAGNI-Prinzip anzuwenden.
  Mit Zitat antworten Zitat
Keks

Registriert seit: 25. Mai 2005
122 Beiträge
 
#16

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 11:25
Wow, Ihr seid super!!
Wieder einiges dazugelernt.

Für meinen aktuellen Fall ist das hier wohl wirklich das beste (natürlich nicht zuletzt, weil es simpel, schnell umsetzbar und kompakt/übersichtlich ist):
Du kannst auch sowas machen:
TTier = (tiHund = 1, tiKatze = 27, tiMaus = 15, ...)
Ich hatte in meinem Eingangsposting ja schon erwähnt, dass ich Aufzählungen grundsätzlich sehr sympatisch finde. Aber ich wusste bis dato nicht, dass man die Indizes darin vorgeben kann (irgendwie bin ich gar nicht auf die Idee gekommen, dass das gehen könnte). Das ist super! Damit funktioniert alles, was ich für den Einsatzzweck benötige. Sogar mit schönen Nebeneffekten, dass z.B. case-Blöcke automatisch mit allen Werten erzeugt werden.

Die Speicherung habe ich nicht thematisiert, sondern deine Verwendung des Begriffs "Konstante", wo du doch eigentlich "Variable" meintest.
Deutsche Sprache <> Delphi.
In der deutschen Sprache sind Interpretationen Gott-Sei-Dank erlaubt. Ich definiere eine 'Konstante' übrigens als Wert, der im Anwendungskontext nicht verändert wird.
Danke, so meinte ich das auch.

Keine Sorge, selbst wenn man die neue erweiterte RTTI fast komplett deaktiviert (was möglich ist), dann funktioniert das immernoch [...]
Ich habe mich gestern Nacht nochmal ein wenig in RTTI eingelesen, aber es ist mir nach wie vor "suspekt". Sorry.

Schnell und simpel ja, allerdings hat man nicht die gesamte Vereinbarung (wie hier mit einem externen System) in einem Objekt gekapselt
In dem speziellen Fall, den ich gerade vor mir habe, kenne ich die Werte schon vorher, muss sie also nicht dynamisch aus der externen Datenquelle zuordnen. Ich muss aber sehr wohl diese Werte einhalten, wenn ich die Daten interpretiere bzw. abspeichere.
Ich muss aber sagen, dass ich die empfohlenen ValueObjects für einen anderen Einsatzzweck, den ich demnächst angehen werde, interesasnt finde. Dort werde ich das etwas komplexer benötigen und da sieht Dein ausführliches Beispiel (Danke!) für mich sehr passend aus.

Ich habe mir mal ein Lesezeichen für diesen Thread gesetzt und werde da sicherlich später für andere Anwendungsfälle wieder nachschlagen.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#17

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 15:52
Schnell und simpel ja, allerdings hat man nicht die gesamte Vereinbarung (wie hier mit einem externen System) in einem Objekt gekapselt
In dem speziellen Fall, den ich gerade vor mir habe, kenne ich die Werte schon vorher, muss sie also nicht dynamisch aus der externen Datenquelle zuordnen. Ich muss aber sehr wohl diese Werte einhalten, wenn ich die Daten interpretiere bzw. abspeichere.
In meinem Beispiel gibt es auch keine externe Datenquelle, von daher sehe ich das jetzt mal nicht als Gegenargument.

Und YAGNI verletze ich damit auch nicht, denn die Klasse macht nur das, was von ihr aktuell gefordert wird:
  • Nur die vorgegebenen Werte zulassen
  • Alle möglichen Werte für eine Auswahl (ComboBox) einfach vorhalten
Bei der Benutzung der Klasse ist ein großer Teil des Drumherums nur dafür da, dass die vorgehaltenen Instanzen nicht von aussen aus Versehen freigegeben werden können und somit die Benutzung intuitiv erfolgen kann ohne sich einen Kopf machen zu müssen (mit ARC würde die Klasse wesentlich kompakter aussehen).

Zudem ist eben die gesamte Vereinbarung enthalten (Interpretation des externen Systems über Integer, Bedeutung für Programmierer/Benutzer als Eigenschaftsname bzw. String) und nicht auf mehrere Stellen verteilt und zur Darstellung benötige ich keine weiteren Hilfsroutinen um z.B. zu einem Integer/Enum einen aussagekräftigen Text zu bekommen. Denn dass 0 hier Hund bedeutet gibt das externe System vor, und dann doch besser an einer Stelle definieren.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Keks

Registriert seit: 25. Mai 2005
122 Beiträge
 
#18

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 17:37
In meinem Beispiel gibt es auch keine externe Datenquelle, von daher sehe ich das jetzt mal nicht als Gegenargument.
Das sollte kein Gegenargument werden, sondern eine Anmerkung zur Einschränkung einer Enumeration. Ein ValueObject ist hier flexibler, denn hier könnte man die Werte auch dynamisch festlegen. Ich wollte damit nur sagen, dass ich dies in meinem Fall kein Entscheidungskriterium war.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#19

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 18:17
In meinem Beispiel gibt es auch keine externe Datenquelle, von daher sehe ich das jetzt mal nicht als Gegenargument.
Das sollte kein Gegenargument werden, sondern eine Anmerkung zur Einschränkung einer Enumeration. Ein ValueObject ist hier flexibler, denn hier könnte man die Werte auch dynamisch festlegen. Ich wollte damit nur sagen, dass ich dies in meinem Fall kein Entscheidungskriterium war.
Ok
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#20

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 23. Jul 2014, 06:12
Ich hatte die Aufgabenstellung nicht so verstanden, das nur ein paar Enumwerte gespeichert/gelesen werden sollen. Aber wenn das alles ist, dann ist die banale, naheliegenste und einfachste Lösung von Nuclearping natürlich zu verwenden.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:42 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