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
Benutzerbild von himitsu
himitsu

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

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 21. Jul 2014, 21:13
Keine Sorge, selbst wenn man die neue erweiterte RTTI fast komplett deaktiviert (was möglich ist), dann funktioniert das immernoch, da diese Funktion auf die alte RTTI aufbaut, welche es schon praktisch immer gibt, da sie von der VCL (FormDesigner) rege verwendet wird.

http://geheimniswelten.de/tipps/code...und-zu-string/
GetEnumName und SetToString (Unit TypInfo), bzw. besser und vorallem (typ)sicherer hinter den Generics versteckt.


Und ich meinte das Colors-Array, welches sich aktuell in UIConsts versteckt und das z.B. von ColorToString verwendet wird.
Natürlich ist dieses Array fest, so daß man (eigentlich) keine eigenen TColor-Konstanten in den DFM-Loader einschleußen kann.
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (21. Jul 2014 um 21:20 Uhr)
  Mit Zitat antworten Zitat
Perlsau
(Gast)

n/a Beiträge
 
#2

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 21. Jul 2014, 21:44
Wieso eine Ini-Datei, wenn es sich doch angeblich um immer gleichbleibende Konstanten handelt? Konstanten wird gewöhnlich ein Wert im Programmcode zugewiesen. Weist du Variablen Werte aus einer Ini-Datei zu, sind das keine Konstanten, sondern eben Variablen, auch wenn diese im gesamten Programmverlauf weitgehend konstant bleiben (sollen), wie z.B. ein Datenbank-Pfad oder der Benutzername usw. Deshalb unterscheidet Delphi doch Konstanten und Variablen, deren Deklaration mit unterschiedlichen Tokens eingeleitet wird: Const und Var.
  Mit Zitat antworten Zitat
Keks

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

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 21. Jul 2014, 22:33
Wieso eine Ini-Datei, wenn es sich doch angeblich um immer gleichbleibende Konstanten handelt? Konstanten wird gewöhnlich ein Wert im Programmcode zugewiesen. Weist du Variablen Werte aus einer Ini-Datei zu, sind das keine Konstanten, sondern eben Variablen, auch wenn diese im gesamten Programmverlauf weitgehend konstant bleiben (sollen), wie z.B. ein Datenbank-Pfad oder der Benutzername usw. Deshalb unterscheidet Delphi doch Konstanten und Variablen, deren Deklaration mit unterschiedlichen Tokens eingeleitet wird: Const und Var.
Letztlich geht es darum, dass der Anwender irgendwelche Auswahlen im Programm trifft, die gespeichert und wieder geladen werden müssen. Um bei dem Beispiel mit den Tieren zu bleiben: Der Anwender wählt selektiert in einer Liste mehrere Tiere. Diese Selektion muss auch später noch geladen und ausgewertet werden können. Es reicht nicht, die Indizes zu speichern, weil sich die Liste jederzeit ändern kann. Un in anderen Fällen brauche ich auch ganz bestimmte Werte, um konsistent mit anderen Programmen, auf die ich keinen Einfluss habe, zu bleiben (wenn man sich eine Datenquelle teilt). Ob das nun Ini- oder DB-Einträge sind, spielt eigentlich keine Rolle.
  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
 
#4

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 21. Jul 2014, 22:56
Das was du meinst ist wohl ein Bei Google suchenValueObject.

Ein Beispiel (zwar PHP sollte aber aussagekräftig genug sein) für Währungen findest du unter
https://github.com/sebastianbergmann...c/Currency.php
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
Perlsau
(Gast)

n/a Beiträge
 
#5

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 03:26
Letztlich geht es darum, dass der Anwender irgendwelche Auswahlen im Programm trifft, die gespeichert und wieder geladen werden müssen. Um bei dem Beispiel mit den Tieren zu bleiben: Der Anwender wählt selektiert in einer Liste mehrere Tiere. Diese Selektion muss auch später noch geladen und ausgewertet werden können. Es reicht nicht, die Indizes zu speichern, weil sich die Liste jederzeit ändern kann. Un in anderen Fällen brauche ich auch ganz bestimmte Werte, um konsistent mit anderen Programmen, auf die ich keinen Einfluss habe, zu bleiben (wenn man sich eine Datenquelle teilt). Ob das nun Ini- oder DB-Einträge sind, spielt eigentlich keine Rolle.
Die Speicherung habe ich nicht thematisiert, sondern deine Verwendung des Begriffs "Konstante", wo du doch eigentlich "Variable" meintest.
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#6

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 06:43
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. Diese Interpretation lässt mir die Freiheit, in einer Spezifikation den Terminus 'Konstante' zu verwenden, ohne gerichtlich einklagbar darauf drängen zu müssen, das dieser Wert im Code auch als 'CONST' deklariert werden muss. Es reicht, wenn er während des Lebenszyklus der Anwendung nicht veränderbar ist, und das schließt initiales Laden aus. Gleichzeitig verbaue ich mir damit nicht die Möglichkeit des initialen Ladens. Aber wenn Du dich dran reibst, dann wende dich an Emba:
Delphi-Quellcode:
Const
  IAmNotAConstant : Integer = 3;

begin
{$J+}
  IAmNotAConstant := 4; // Verbooooten !!!1!!!!11!!!EINS!!!11!!ELF!!!
Im Übrigen bin ich auch für korrekte Terminologie, aber nur, wenn es um wichtige Dinge geht. Auch kann ich verstehen, das in der reinen Sprachenlehre eine Konstante nicht immer eine Konstante ist. Aber dann ist sie eigentlich auch keine Variable, denn verändert wird sie nicht (nur initialiert).
Zitat von Heraklit von Ephesus:
Die einzige Konstante im Universum ist die Veränderung.
Zum Thema: Ich hätte da noch statische Klasseneigengeschaften anzubieten. Bei Applikationskonstanten etwa so:
Delphi-Quellcode:
Type
Unit ApplicationSettings;
Interface
Type
  TApplicationContext = Class
  private
    class var fMySetting : string;
    class var fSomeOtherValue : Integer;
    class procedure Load();
  public
    Class property MySetting : String read fMySetting;
    class property SomeOtherValue : Integer read fSomeOtherValue;

  end;
...
implementation

...
initialization
  TApplicationContext.Load();
end.
Diese Unit bindest Du einfach immer dann ein, wenn Du Zugriff auf die Applikationskonstanten benötigst. Du hast hier dann alles, was Du brauchst. Damit wäre deine erste Vorgabe erfüllt:
Zitat:
Die Werte sollten zudem irgendwie gruppiert sein, damit man damit leichter arbeiten kann (Autovervollständigung, Parameter-Eingrenzung).
Für dein konkretes Beispiel der Tiere (das nur bedingt etwas mit dem generellen Problem der Applikationskonstanten zu tun hat) sollte man zunächst klarstellen, das die einzelnen Tiere eindeutige Identifikatoren (Id) besitzen, die eben nicht änderbar sind (also eine Perlsau'sche Konstante). Hier hat man immer das Dilemma, das man einfach darauf hoffen muss, das sich die Anwendungen an die Konvention halten, einem Hund immer die ID 1 zuzuweisen usw.
Ich würde meine TTier-Klasse wie folgt umsetzen, und dabei die von Sir Rufo vorgeschlagenen value objects einsetzen. Die Methode 'GetTierName' gehört dann zum Tier (OOP) und ist keine isolierte Funktion (PP)
Delphi-Quellcode:
Type
  TTier = class
  private
    fID: Integer;
    fName : String;
    class var fKatze: TTier;
    class var fHund: TTier;
    class procedure Load;
    constructor Create (aID : Integer; Name : String);
  public
    property ID : Integer read fID;
    property Name : String read fName;

    class property Hund : TTier Read fHund;
    class property Katze : TTier Read fKatze;
  end;

{ TTier }

constructor TTier.Create(aID: Integer);
begin
  fID := aID;
end;

class procedure TTier.Load;
begin
// Natürlich wird hier aus einer Datei/Registry/Datenbank geladen
  fHund := TTier.Create(3,'Hund');
  fKatze := TTier.Create(4,'Mitzekatze');
end;

initialization
  TTier.Load;
end.
Und damit solltest Du alle deine Wünsche umsetzen können, denn eine Katze ist ein Tier, konstant, die Id ist Perlsau-konform (sofern man die Datei nicht verändert, siehe Heraklit) und deine Tiere können um Methoden und Eigenschaften erweitert werden, was bei einem Enum nicht geht. Ich persönlich halte auch nicht viel von RTTI-Geraffel im Hintergrund.
  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
 
#7

AW: Verständnisproblem: Globale, gruppierte Konstanten

  Alt 22. Jul 2014, 08: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 08:30 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


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 15:02 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 by Thomas Breitkreuz