|
![]() |
|
Dejan Vu
(Gast)
n/a Beiträge |
#1
Die Speicherung habe ich nicht thematisiert, sondern deine Verwendung des Begriffs "Konstante", wo du doch eigentlich "Variable" meintest.
![]() 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:
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).
Const
IAmNotAConstant : Integer = 3; begin {$J+} IAmNotAConstant := 4; // Verbooooten !!!1!!!!11!!!EINS!!!11!!ELF!!! ![]() Die einzige Konstante im Universum ist die Veränderung.
Delphi-Quellcode:
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:
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. ![]() Die Werte sollten zudem irgendwie gruppiert sein, damit man damit leichter arbeiten kann (Autovervollständigung, Parameter-Eingrenzung).
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:
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.
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. |
![]() |
Registriert seit: 5. Jan 2005 Ort: Stadthagen 9.454 Beiträge Delphi 10 Seattle Enterprise |
#2
Hier ein ValueObject in Aktion:
Delphi-Quellcode:
und die Ausgabe
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.
Code:
Ein robustes ValueObject bedarf unter Delphi allerdings einiges an zusätzlichem Gebimmel-Bammel, da wir ja auf die lifetime der Instanzen achtgeben müssen.
Hund
Katze Maus Wie ein/e Maus Hier ist ein/e Hund Hier ist immer noch ein/e Hund EArgumentOutOfRangeException: Ungültige TierId 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) |
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |