Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht (https://www.delphipraxis.net/175091-statt-x-fache-%84if-then-else%93-abfragen-eine-intelligentere-loesung-gesucht.html)

juergen 29. Mai 2013 20:17

statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Hallo zusammen,

wer kennt das nicht: Man fängt ein Projekt an und dann wird im Laufe der Zeit immer mehr hinten "dran gebastelt".
Ich bin nun an einem Punkt wo ich gar keine Lust mehr habe weiter zu machen, da eine bestimmte Procedure total unübersichtlich ist.

Folgende Gegebenheiten zur Umbenennung von Dateien:
Ich habe als Basis ca. 8 verschiedene MP3-Tag's (Interpret, Titel, Album usw.). Der Anwender kann momentan in einer Radio-Groupbox von ca. 10 Möglichkeiten eine Auswahl treffen, wie der Dateiname zusammengesetzt werden soll, also
a) welche MP3-Tag's überhaupt verwendet werden sollen
b) in welcher Reihenfolge der Dateiname zusammengestellt werden soll. Z.B. Interpret - Titel.mp3 oder Album - CdNr - TrackNr - Interpret - Titel.mp3

Dabei nutze ich als "Trenner" im Dateinamen jeweils " - " (Leerzeichen Bindestrich Leerzeichen).
Momentan habe ich für die gebräuchlichsten Dateinamen-Muster ein Radio-Group-Item erstellt und prüfe jedes einzelne Item über eine If then else Abfrage!
Der ganze Code wiederholt sich dann pro If-Abfrage, nur die Reihenfolge und die Anzahl der MP3-Tag's sind halt in jeder If-Abfrage anders.
Ich möchte dem Anwender keine RegEx-Ausdrücke oder dergleichen anbieten, es soll eben EINFACH und durch Lieschen Müller bedien bar sein.

Ich habe einige "nebulöse" Ansätze, wollte aber erst mal hier die Profis fragen wie sowas am besten umgesetzt werden kann um meinen völlig unübersichtlichen und sich ständig wiederholenden Code endlich richtig aufzuräumen.


Für Denkanstöße wäre ich sehr dankbar!

lbccaleb 29. Mai 2013 20:25

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Also ich würde wohl so vorgehen, dass ich die CheckBoxen in einer Case Anweisung abfrage, und für die sich wiederholenden if Klauseln, eine separate Funktion erstelle.

Mehr fällt mir im mom auch nicht ein :D

juergen 29. Mai 2013 20:39

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
@lbccaleb,
danke für deine Antwort. :thumb:
Genau das war auch mein erster Ansatz. Ich hätte dann pro Checkbox eine Funktion.
Ich hoffe auf einen "dynamischere" Lösung, quasi nur eine Procedur oder dergleichen, welche bestimmte Parameter übergeben bekommt und dann alles richtig zusammenstellt....
Aber wie genau das gehen soll, da fehlt mir moementan der Ansatz.

lbccaleb 29. Mai 2013 20:45

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Da kann man bestimmt auch weiter helfen, da müsste man aber halt wohl ein wenig sehen was genau du in deiner jetzigen Funktion/Procedure machst. Dann kann man das bestimmt ein wenig zusammen fassen und ggf. in separate Funktionen packen.

jfheins 29. Mai 2013 20:51

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Auch wenn du dem Benutzer keine RegExen aufbürden willst, so kannst du das doch im Code verwenden.

Du kannst ja jedem RadioItem einen String zuweisen wie "<<I>> - <<T>> (<<Y>>)". (Zur Not über die Tag-Property und ein separates Array)
Anschließend lässt du für den konkreten Dateinamen einfach ein Suchen&Ersetzen über diesen String früberlaufen. <<I>> wird durch den Interpreten ersetzt usw.

Oder du benutzt direkt die integrierte string-Format Funktion, die kann ja auch bestimmte Indices ansprechen.

Sir Rufo 29. Mai 2013 22:12

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Baue dir doch einen Builder, der eben diese Dateinamen erstellt.

Die gewünschten Teile (Interpret, Titel, ...) und deren Reihenfolge einfach dem Builder übergeben und dann damit durch die Dateien laufen.
Für Vorbelegungen (Radiogroup) kannst du pro Eintrag eine Builder-Instanz hinterlegen und diese dann benutzen.

Delphi-Quellcode:
unit FilenameBuilder;

interface

uses
  Classes,
  Generics.Collections,
  SysUtils;

type
  TMP3Meta = record
    Interpret : string;
    Titel : string;
    Album : string;
    CdNr : Integer;
    TrackNr : Integer;
  end;

  IFilenamePart = interface
    ['{281FA130-B641-454D-8E9A-58D0C02F4C14}']
    function Build( const AData : TMP3Meta ) : string;
  end;

  TFilenamePartFactory = class
  private
    FParts : TDictionary<string, IFilenamePart>;
  public
    constructor Create;
    destructor Destroy; override;

    function GetPart( const AKey : string ) : IFilenamePart;
  end;

  TFilenameBuilder = class
  private
    FParts : TList<IFilenamePart>;
  public
    constructor Create;
    destructor Destroy; override;

    procedure AddPart( APart : IFilenamePart );
    procedure Clear;

    function GetFilename( const AData : TMP3Meta ) : string;
  end;

implementation

type
  TFilenamePart = class( TInterfacedObject, IFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; virtual; abstract;
  end;

  TInterpretPart = class( TFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  end;

  TTitelPart = class( TFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  end;

  TAlbumPart = class( TFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  end;

  TCdNrPart = class( TFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  end;

  TTrackNrPart = class( TFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  end;

  { TFilenameBuilder }

procedure TFilenameBuilder.AddPart( APart : IFilenamePart );
begin
  FParts.Add( APart );
end;

procedure TFilenameBuilder.Clear;
begin
  FParts.Clear;
end;

constructor TFilenameBuilder.Create;
begin
  inherited Create;
  FParts := TList<IFilenamePart>.Create;
end;

destructor TFilenameBuilder.Destroy;
begin
  FParts.Free;
  inherited;
end;

function TFilenameBuilder.GetFilename( const AData : TMP3Meta ) : string;
var
  LBuilder : TStringBuilder;
  LIdx :    Integer;
begin
  LBuilder := TStringBuilder.Create;
  try
    for LIdx := 0 to FParts.Count - 1 do
      begin
        if LIdx > 0
        then
          LBuilder.Append( '-' );

        LBuilder.Append( FParts[LIdx].Build( AData ) );
      end;

    Result := LBuilder.ToString;

  finally
    LBuilder.Free;
  end;
end;

{ TInterpretPart }

function TInterpretPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := AData.Interpret;
end;

{ TTitelPart }

function TTitelPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := AData.Titel;
end;

{ TAlbumPart }

function TAlbumPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := AData.Album;
end;

{ TCdNrPart }

function TCdNrPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := IntToStr( AData.CdNr );
end;

{ TTrackNrPart }

function TTrackNrPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := IntToStr( AData.TrackNr );
end;

{ TFilenamePartFactory }

constructor TFilenamePartFactory.Create;
begin
  inherited Create;
  FParts := TDictionary<string, IFilenamePart>.Create;

  FParts.Add( 'Interpret', TInterpretPart.Create );
  FParts.Add( 'Titel', TTitelPart.Create );
  FParts.Add( 'Album', TAlbumPart.Create );
  FParts.Add( 'CdNr', TCdNrPart.Create );
  FParts.Add( 'TrackNr', TTrackNrPart.Create );
end;

destructor TFilenamePartFactory.Destroy;
begin
  FParts.Free;
  inherited;
end;

function TFilenamePartFactory.GetPart( const AKey : string ) : IFilenamePart;
begin
  Result := FParts[AKey];
end;

end.
Das sieht zwar wild aus, ist aber in der Anwendung relativ zahm
Delphi-Quellcode:
program FilenameBuilderTest;

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

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

var
  PartFactory : TFilenamePartFactory;

procedure Test;
var
  LFilenameBuilder : TFilenameBuilder;
  LData :           TMP3Meta;
begin

  LData.Interpret := 'Testinterpret';
  LData.Titel    := 'Testtitel';
  LData.Album    := 'TestAlbum';
  LData.CdNr     := 1;
  LData.TrackNr  := 10;

  LFilenameBuilder := TFilenameBuilder.Create;
  try

    LFilenameBuilder.AddPart( PartFactory.GetPart( 'TrackNr' ) );
    LFilenameBuilder.AddPart( PartFactory.GetPart( 'CdNr' ) );
    LFilenameBuilder.AddPart( PartFactory.GetPart( 'Album' ) );
    LFilenameBuilder.AddPart( PartFactory.GetPart( 'Interpret' ) );
    LFilenameBuilder.AddPart( PartFactory.GetPart( 'Titel' ) );

    WriteLn( LFilenameBuilder.GetFilename( LData ) );

    LFilenameBuilder.Clear;

    LFilenameBuilder.AddPart( PartFactory.GetPart( 'CdNr' ) );
    LFilenameBuilder.AddPart( PartFactory.GetPart( 'TrackNr' ) );
    LFilenameBuilder.AddPart( PartFactory.GetPart( 'Interpret' ) );
    LFilenameBuilder.AddPart( PartFactory.GetPart( 'Titel' ) );

    WriteLn( LFilenameBuilder.GetFilename( LData ) );

  finally
    LFilenameBuilder.Free;
  end;

end;

begin
  try

    PartFactory := TFilenamePartFactory.Create;
    try

      Test;

    finally
      PartFactory.Free;
    end;

  except
    on E : Exception do
      WriteLn( E.ClassName, ': ', E.Message );
  end;

  ReadLn;

end.

juergen 30. Mai 2013 15:11

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
@jfheins,
Danke für deine Idee. :thumb: Ich denke so werde ich es nun erstmal umsetzen.

@Sir Rufo,
Vielen Dank für deine Mühe!:thumb:
Da ich für dieses Projekt noch D2007 nutze wo es die Generics.Collections nicht gibt, kann ich das momentan so nicht umsetzen.
Einiges von deinem Code habe ich verstanden, einiges aber auch nicht.:oops:
Deine Mühe war nicht umsonst, da ich versuchen möchte deine Art der Umsetzung zu verstehen!

Was ich momentan gar nicht verstehe ist folgendes:
Delphi-Quellcode:
  IFilenamePart = interface
     ['{281FA130-B641-454D-8E9A-58D0C02F4C14}']
     function Build( const AData : TMP3Meta ) : string;
   end;
In der Hilfe zu interface steht, dass eine mit einem Interface-Typ deklarierte Variable Instanzen jeder Klasse referenzieren kann.
Ich denke dass IFilenamePart diese Variable ist. Aber wozu dient dieser seltsame Zahlen/Buchstaben-String ({281FA130-B641-454D-8E9A-58D0C02F4C14})?
Ist das eine Vorbelegung der Variable, damit diese von Anbeginn nicht leer ist?

Grüße Jürgen

stahli 30. Mai 2013 15:30

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Als generelle Anmerkung noch folgendes:

Man kann in einer Funktion statt vieler if then else ggf. auch mit Exit arbeiten:

Delphi-Quellcode:
function Test: Integer;
begin
  if a then
    Exit(1);
  if b then
    Exit(2);
  if c then
    Exit(3);
end;
Wenn zum Schluss noch etwas erledigt werden muss hilft try finally:

Delphi-Quellcode:
function Test: Integer;
begin
  try
    if a then
      Exit(1);
    if b then
      Exit(2);
    if c then
      Exit(3);
  finally
    Print(Result);
  end;
end;
Das ist m.E. etwas schlanker als viele if then else.
(Wobei manche Entwickler Exit eher ablehnen.)

Sir Rufo 30. Mai 2013 15:54

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Zitat:

Zitat von juergen (Beitrag 1216985)
Was ich momentan gar nicht verstehe ist folgendes:
Delphi-Quellcode:
  IFilenamePart = interface
     ['{281FA130-B641-454D-8E9A-58D0C02F4C14}']
     function Build( const AData : TMP3Meta ) : string;
   end;
In der Hilfe zu interface steht, dass eine mit einem Interface-Typ deklarierte Variable Instanzen jeder Klasse referenzieren kann.

Ja, jeder Klasse kannst du auch noch ein oder mehrere Interfaces aufdrücken, allerdings muss die Klasse die Methoden vom Interface auch implementieren.
Zitat:

Zitat von juergen (Beitrag 1216985)
Ich denke dass IFilenamePart diese Variable ist.

Nö, das ist die Deklaration des Interfaces
Zitat:

Zitat von juergen (Beitrag 1216985)
Aber wozu dient dieser seltsame Zahlen/Buchstaben-String ({281FA130-B641-454D-8E9A-58D0C02F4C14})?

Der seltsame Zahlen/Buchstaben-String ist eine Bei Google suchenGUID und stellt einfach nur eine eindeutige Kennzeichnung für das Interface dar.
Die sollte da sein, ist aber erstmal nicht weiter von Belang (tu so, als ob die nicht da wäre ;))
Zitat:

Zitat von juergen (Beitrag 1216985)
Ist das eine Vorbelegung der Variable, damit diese von Anbeginn nicht leer ist?

Nein :)

Ohne Generics geht das auch - ist aber nicht so schön :)

Mal sehen, ob ich das so auf die Schnelle hingezaubert bekomme ...

Sir Rufo 30. Mai 2013 15:59

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Sodele hier ohne Generics
Delphi-Quellcode:
unit FilenameBuilder;

interface

uses
  Classes,
  SysUtils;

type
  TMP3Meta = record
    Interpret : string;
    Titel : string;
    Album : string;
    CdNr : Integer;
    TrackNr : Integer;
  end;

  IFilenamePart = interface
    ['{281FA130-B641-454D-8E9A-58D0C02F4C14}']
    function Build( const AData : TMP3Meta ) : string;
  end;

  TFilenameBuilder = class
  private
    FParts : TInterfaceList;
  public
    constructor Create;
    destructor Destroy; override;

    procedure AddPart( APart : IFilenamePart );
    procedure Clear;

    function GetFilename( const AData : TMP3Meta ) : string;
  end;

type
  TFilenamePart = class( TInterfacedObject, IFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; virtual; abstract;
  end;

  TInterpretPart = class( TFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  end;

  TTitelPart = class( TFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  end;

  TAlbumPart = class( TFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  end;

  TCdNrPart = class( TFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  end;

  TTrackNrPart = class( TFilenamePart )
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  end;

implementation

{ TFilenameBuilder }

procedure TFilenameBuilder.AddPart( APart : IFilenamePart );
begin
  FParts.Add( APart );
end;

procedure TFilenameBuilder.Clear;
begin
  FParts.Clear;
end;

constructor TFilenameBuilder.Create;
begin
  inherited Create;
  FParts := TInterfaceList.Create;
end;

destructor TFilenameBuilder.Destroy;
begin
  FParts.Free;
  inherited;
end;

function TFilenameBuilder.GetFilename( const AData : TMP3Meta ) : string;
var
  LBuilder : TStringBuilder;
  LIdx :    Integer;
begin
  LBuilder := TStringBuilder.Create;
  try
    for LIdx := 0 to FParts.Count - 1 do
      begin
        if LIdx > 0
        then
          LBuilder.Append( '-' );

        LBuilder.Append( ( FParts[LIdx] as IFilenamePart ).Build( AData ) );
      end;

    Result := LBuilder.ToString;

  finally
    LBuilder.Free;
  end;
end;

{ TInterpretPart }

function TInterpretPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := AData.Interpret;
end;

{ TTitelPart }

function TTitelPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := AData.Titel;
end;

{ TAlbumPart }

function TAlbumPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := AData.Album;
end;

{ TCdNrPart }

function TCdNrPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := IntToStr( AData.CdNr );
end;

{ TTrackNrPart }

function TTrackNrPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := IntToStr( AData.TrackNr );
end;

end.
und die kleine Demo
Delphi-Quellcode:
program FilenameBuilderTest;

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

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

procedure Test;
var
  LFilenameBuilder : TFilenameBuilder;
  LData :           TMP3Meta;
begin

  LData.Interpret := 'Testinterpret';
  LData.Titel    := 'Testtitel';
  LData.Album    := 'TestAlbum';
  LData.CdNr     := 1;
  LData.TrackNr  := 10;

  LFilenameBuilder := TFilenameBuilder.Create;
  try

    LFilenameBuilder.AddPart( TTrackNrPart.Create );
    LFilenameBuilder.AddPart( TCdNrPart.Create );
    LFilenameBuilder.AddPart( TAlbumPart.Create );
    LFilenameBuilder.AddPart( TInterpretPart.Create );
    LFilenameBuilder.AddPart( TTitelPart.Create );

    WriteLn( LFilenameBuilder.GetFilename( LData ) );

    LFilenameBuilder.Clear;

    LFilenameBuilder.AddPart( TCdNrPart.Create );
    LFilenameBuilder.AddPart( TTrackNrPart.Create );
    LFilenameBuilder.AddPart( TInterpretPart.Create );
    LFilenameBuilder.AddPart( TTitelPart.Create );

    WriteLn( LFilenameBuilder.GetFilename( LData ) );

  finally
    LFilenameBuilder.Free;
  end;

end;

begin
  try

    Test;

  except
    on E : Exception do
      WriteLn( E.ClassName, ': ', E.Message );
  end;

  ReadLn;

end.

juergen 30. Mai 2013 17:37

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
@stahli,
danke für deinen Hinweis. :thumb:
Das kenne ich, mein Problem ist der sich x-facch wiederholdende *fast* gleiche Code in jeder einzelnen If-Klausel. Eine If Klausel hat ca. 300 Zeilen.....
Deswegen mein Ansporn das diese 300 Zeilen nur noch einmal aufgerufen werden und der Dateiname anhand der Userauswahl vorher zusammengestellt wird und dann in dieser einen Procedure genutzt werden kann ohne If-Klauseln für die jeweilige Userauswahl.

@Sir Rufo,
Erst einmal vielen Dank für die aufklärenden Worte im Post #9!:thumb:
Da lag ich ja fast..... ähhh nein ganz daneben mit meiner Annahme auf die Variable bezogen. :oops: Aber wer nicht fragt...

Auch für dein Beispeil ohne Generics vielen Dank! Im Moment baue ich noch einiges anderes um. Sobald ich dann ans Aufräumen für das hier besprochene Thema gehe, werde ich mir dein Code ganz genau Schritt für Schritt anschauen. Ich vermute, dass sich mir dabei noch ein paar Fragen stellen werden.

Bis hierhin tausend Dank und noch einen schönen Feiertag (zumindest in NRW)!

Gruß
Jürgen

juergen 30. Mai 2013 23:38

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
@Sir Rufo,

habe jetzt deine Variante mit kleineren Anpassungen erfolgreich umgesetzt. :-D
Läuft super und ich habe einiges gelernt!

Nochmals Danke!:dp:

BUG 31. Mai 2013 02:31

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Zitat:

Zitat von juergen (Beitrag 1217039)
Läuft super und ich habe einiges gelernt!

Ein weiterer jetzt möglicher Schritt wäre das Erstellen eines Builders aus einer Zeichenkette.

Dazu müsstest du einen einfachen Parser schreiben, der für jede "Variable" (zB. %TITLE%) den passenden Part erstellt und zu einem Builder hinzufügt.
Dabei wäre es günstig, einen neuen Part für konstante Teile des Dateinamens zu implementieren (zB. "-miep-" in "%INTERPRET%-miep-%TITLE%"). Der gibt immer den gleichen String zurück.
Damit könnte der Anwender den Pfad dann frei konfigurieren :cyclops:

Zitat:

Zitat von juergen (Beitrag 1216912)
Ich möchte dem Anwender keine RegEx-Ausdrücke oder dergleichen anbieten, es soll eben EINFACH und durch Lieschen Müller bedien bar sein.

Es ist als erfahrener Anwender immer schön, wenn man zusätzlich zu sinnvollen Voreinstellungen an solchen Sachen schrauben kann.


Abgesehen davon könnte man auch eine grafische Oberfläche zum einfachen Zusammensetzen eines Builders anbieten.

Furtbichler 31. Mai 2013 07:25

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Zitat:

Zitat von BUG (Beitrag 1217041)
Dazu müsstest du einen einfachen Parser schreiben, der für jede "Variable" (zB. %TITLE%) den passenden Part erstellt und zu einem Builder hinzufügt.

Der 'Parser' sieht dann z.B. so aus:
Delphi-Quellcode:
Filename := Replace (Filename,'%TITLE%',MyMusic.Title,[rfReplaceAll]);
Filename := Replace (Filename,'%INTERPRET%',MyMusic.Interpret,[rfReplaceAll]);
Filename := Replace (Filename,'%DATE%',DateToStr(Date),[rfReplaceAll]);
...
Allerdings ist das kein Parser, sondern etwas viel einfacheres: Ein String-Austausch-O-Mat.

Diese Vorgehensweise halte ich persönlich für die flexibelste, weil jederzeit Variablen (bzw. eigentlich ja Platzhalter) hinzugefügt werden können.

Eine fortlaufende Numerierung? Nennen wir sie '%NUMBER%'
Delphi-Quellcode:
Filename := Replace (Filename,'%NUMBER%',IntToStr(Number),[rfReplaceAll]);
Inc(Number);
Das gleiche, nur hier soll die Nummer nur erhöht werden, wenn der Platzhalter existiert.
Delphi-Quellcode:
if Pos ('%NUMBER%', Filename)>0 then
begin
  Filename := Replace (Filename,'%NUMBER%',IntToStr(Number),[rfReplaceAll]);
  inc(Number);
end;
Usw.

Du kannst das dann auch auf die Spitze treiben, indem Du einen Skriptinterpreter einbindest. Dann kann der Anwender seine eigenen Namensregeln festlegen. Braucht zwar kaum einer, aber Spaß macht es trotzdem.

BUG 1. Jun 2013 10:16

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Zitat:

Zitat von Furtbichler (Beitrag 1217052)
Allerdings ist das kein Parser, sondern etwas viel einfacheres: Ein String-Austausch-O-Mat.

Das ist allerdings auch nicht das, was ich vorgeschlagen habe :stupid:
Ich habe das Erstellen eines Builder-Objekts aus dem String vorgeschlagen.

juergen 1. Jun 2013 18:19

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
@BUG,

so wie ich dich verstanden habe schlägst du vor mit reservierten Wörtern zu arbeiten (z.B. "miep").
Das ist jetzt schon ziemlich einfach in dem aufgezeigten FileNameBuilder von Sir Rufo umsetzbar. :-D
Dazu würde ich einfach einen neuen TFileNamePart erstellen und entsprechend anwenden.
Oder habe ich dich jetzt falsch verstanden?

BUG 2. Jun 2013 12:36

AW: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht
 
Zitat:

Zitat von juergen (Beitrag 1217212)
Dazu würde ich einfach einen neuen TFileNamePart erstellen und entsprechend anwenden.

Ich hatte mir so etwas vorgestellt:
Delphi-Quellcode:
unit FilenameBuilder;

interface

uses
  Classes,
  Generics.Collections,
  SysUtils;

// ...

  TFilenamePartFactory = class
  private
    FParts : TDictionary<string, IFilenamePart>;
  public
    constructor Create;
    destructor Destroy; override;

    function GetPart( const AKey : string ) : IFilenamePart;
    // >>
    function GetConstantPart( const AString : string ) : IFilenamePart;
   // <<
  end;

// ...

implementation

// ...

  TConstantPart = class( TFilenamePart )
  public
    contructor create(AString: String);
  protected
    function Build( const AData : TMP3Meta ) : string; override;
  private
    constantString: string;
  end;

// ...

function TFilenameBuilder.GetFilename( const AData : TMP3Meta ) : string;
var
  LBuilder : TStringBuilder;
  LIdx : Integer;
begin
  LBuilder := TStringBuilder.Create;
  try
    for LIdx := 0 to FParts.Count - 1 do
      begin
        // "-" entfernt
        LBuilder.Append( FParts[LIdx].Build( AData ) );
      end;

    Result := LBuilder.ToString;

  finally
    LBuilder.Free;
  end;
end;

// ...

{ TConstantPart }

constructor TConstantPart.create(AString: string)
begin
  constantString := AString;
end;

function TConstantPart.Build( const AData : TMP3Meta ) : string;
begin
  Result := constantString;
end;

{ TFilenamePartFactory }

// ...

function TFilenamePartFactory.GetConstantPart( const AString : string ) : IFilenamePart;
begin
  Result := TConstantPart.create(AString);
end;

end.
Dann könnte man wie folgt für den Builder eine Factory schreiben:
Delphi-Quellcode:
type TFilenameBuilderFactory = class
  public
    constructor create(AFactory: TFilenamePartFactory; ownsFactory: boolean = true);
    destructor destroy; override;
   
    (** Returns a *new* object, caller handles destruction. *)
    function createBuilder(AString : string): TFilenameBuilder;

  private
    FFactory: TFilenamePartFactory;
    FOwnsFactory: boolean;
    // non-constant state
    FInVariable: boolean;
    FBuffer: string; // invariant: length(FBuffer) = 0
    FBuilder: TFilenameBuilder; // invariant: FBuilder = nil
    // methods
    procedure flushBuffer;
  end;

constructor TFilenameBuilderFactory.create(AFactory: TFilenamePartFactory; ownsFactory: boolean);
begin
  FFactory = AFactory;
  FOwnsFactory = ownsFactory;
  setLength(buffer, 0);
  FBuilder = nil;
end;

destructor TFilenameBuilderFactory.destroy
begin
  if FOwnsFactory then FFactory.free();
end;

procedure TFilenameBuilderFactory.flushBuffer;
var
  tempPart: IFilenamePart;
begin
  if (length(FBuffer) > 0) then
  begin
    if inVariable
      then tempPart := AFactory.GetPart(FBuffer) // not sure what TDirectory.getItem does when key does not exist!!!
      else tempPart := AFactory.GetConstantPart(FBuffer);
    ABuilder.add(tempPart);
  end;
  setLength(FBuffer, 0);
end;

function TFilenameBuilderFactory.createBuilder(AString : string): TFilenameBuilder;
var
  position: integer;
begin
  FBuilder = TFilenameBuilder.create();
  inVariable = false;
  for position := 1 to length(string) do
  begin
    if (AString[position] = '%') then
    begin
      flushBuffer();
      inVariable = not inVariable;
    end else begin
      FBuffer := FBuffer + AString[position];
    end;
  end;
  flushBuffer();
  result = FBuilder;
  FBuilder = nil;
end;
Dar Parser lässt noch viel Raum für Verbesserung, sollte aber als zur Verdeutlichung taugen.


Achtung:
Ich habe lange kein Delphi mehr benutzt, sind also wahrscheinlich Fehler drin.
Anmerkung zur Code-Qualität sind natürlich erwünscht :mrgreen:


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:11 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-2025 by Thomas Breitkreuz