Thema: Delphi Generics und Enums

Einzelnen Beitrag anzeigen

Benutzerbild von Sir Rufo
Sir Rufo

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

AW: Generics und Enums

  Alt 12. Sep 2015, 14:45
Das Pizza-Beispiel ist schlecht, denn niemand würde einen Pizza-Service schreiben, wo die Toppings als Enums deklariert sind.

Kommen wir mal zu diesem Beispiel, eine Ampelschaltung mit der StateMachine (auch die kann erweitert werden):
Delphi-Quellcode:
program Project3;

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

uses
  System.SysUtils,
  Stateless,
  Stateless.Utils;

type
{$IFDEF USE_ENUMS}
{$SCOPEDENUMS ON}
  TState = (
    {} Red,
    {} RedYellow,
    {} Yellow,
    {} Green );
  TTrigger = ( Timer );
{$ELSE}
  TState = type string;

  TStateHelper = record helper for TState
  const
    Red = 'Red';
    RedYellow = 'RedYellow';
    Yellow = 'Yellow';
    Green = 'Green';
  end;

  TTrigger = type string;

  TTriggerHelper = record helper for TTrigger
  const
    Timer = 'Timer';
  end;
{$ENDIF}

  TTrafficLight = TStateMachine<TState, TTrigger>;

  TTrafficLightData = class
  private
    FState: TState;
  public
    property State: TState read FState write FState;
  end;

procedure Test( AData: TTrafficLightData );
var
  LLight: TTrafficLight;
  LIdx : Integer;
begin
  LLight := TTrafficLight.Create(
    function: TState
    begin
      Result := AData.State;
    end,
    procedure( const s: TState )
    begin
      AData.State := s;
    end );
  try

    LLight.Configure( TState.Red )
    {} .Permit( TTrigger.Timer, TState.RedYellow );
    LLight.Configure( TState.RedYellow )
    {} .Permit( TTrigger.Timer, TState.Green );
    LLight.Configure( TState.Green )
    {} .Permit( TTrigger.Timer, TState.Yellow );
    LLight.Configure( TState.Yellow )
    {} .Permit( TTrigger.Timer, TState.Red );

    Writeln( LLight.ToString );
    for LIdx := 1 to 10 do
      begin
        LLight.Fire( TTrigger.Timer ); // ohne Enum und 'Blue' kommt erst hier die Exception
        Writeln( LLight.ToString );
      end;

  finally
    LLight.Free;
  end;
end;

procedure TestStart;
const
  InitialStateString = 'Blue'; // Wert wird aus der Datenbank gelesen
var
  LData: TTrafficLightData;
begin
  LData := TTrafficLightData.Create;
  try
{$IFDEF USE_ENUMS}
    LData.State := TEnum.ToEnum<TState>( InitialStateString ); // Wirft eine Exception bei 'Blue'
{$ELSE}
    LData.State := InitialStateString; // 'Blue' wird anstandslos akzeptiert
{$ENDIF}
    Test( LData );
  finally
    LData.Free;
  end;
end;

begin
  try
    TestStart;
  except
    on E: Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;
  Readln;

end.
In diesem (speziellen) Fall bringt mir der Enum den Vorteil, dass fehlerhafte Daten 'Blue' sehr früh als solche mir um die Ohren fliegen und auch sehr nah an der Quelle des Übels.

Bei der Definition der Konstanten kann man so einen Fehler machen
Delphi-Quellcode:
  TStateHelper = record helper for TState
  const
    Red = 'Red';
    RedYellow = 'Red'; // CopyPaste-Fehler durch den Programmierer
    Yellow = 'Yellow';
    Green = 'Green';
  end;
der sich aber anstandslos kompilieren lässt. Wenn ich Glück habe, fällt dies zur Laufzeit auf, wenn ich Pech habe läuft einfach alles nur Grütze und keiner weiß warum - beten wir, dass wir einen Unittest haben, der diesen Fehler aufdeckt.

Bei einem Enum geht das nicht
Delphi-Quellcode:
TState = (
  {} Red,
  {} Red, // CopyPaste-Fehler durch den Programmierer
  {} Yellow,
  {} Green );
denn da schreit einen der Compiler direkt beim Kompilieren an.

Zitat von Meine Meinung:
Es geht nicht darum, dass Enums besser als Konstanten sind.

Es gibt aber Fälle (s.o.), wo ein Enum besser ist als Konstanten.
Genau wie es Fälle gibt, wo Konstanten besser als Enums sind.
Und es gibt Fälle, da sind weder Enums noch Konstanten angebracht (s. Pizza-Toppings).

Man muss wissen, welche Vor- und Nachteile das eine oder das andere mit sich bringt und dann eine Entscheidung treffen und mit dieser und den daraus resultierenden Nachteilen leben - Refactoring geht ja auch immer.
Ich würde es gerne noch größer und fetter und bunter und noch auffälliger schreiben, aber ich bin hier in den Möglichkeiten durch das Forum beschränkt
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 (12. Sep 2015 um 15:11 Uhr)
  Mit Zitat antworten Zitat