AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Delphi statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht

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

Ein Thema von juergen · begonnen am 29. Mai 2013 · letzter Beitrag vom 2. Jun 2013
Antwort Antwort
Benutzerbild von Sir Rufo
Sir Rufo

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

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

  Alt 29. Mai 2013, 22:12
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.
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 (29. Mai 2013 um 22:19 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von juergen
juergen

Registriert seit: 10. Jan 2005
Ort: Bönen
1.176 Beiträge
 
Delphi 11 Alexandria
 
#2

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

  Alt 30. Mai 2013, 15:11
@jfheins,
Danke für deine Idee. Ich denke so werde ich es nun erstmal umsetzen.

@Sir Rufo,
Vielen Dank für deine Mühe!
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.
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
Jürgen
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.359 Beiträge
 
Delphi 11 Alexandria
 
#3

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

  Alt 30. Mai 2013, 15:30
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.)
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  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: statt x-fache „if then else“-Abfragen eine intelligentere Lösung gesucht

  Alt 30. Mai 2013, 15:54
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.
Ich denke dass IFilenamePart diese Variable ist.
Nö, das ist die Deklaration des Interfaces
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 )
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 ...
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
Benutzerbild von Sir Rufo
Sir Rufo

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

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

  Alt 30. Mai 2013, 15:59
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.
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
Benutzerbild von juergen
juergen

Registriert seit: 10. Jan 2005
Ort: Bönen
1.176 Beiträge
 
Delphi 11 Alexandria
 
#6

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

  Alt 30. Mai 2013, 17:37
@stahli,
danke für deinen Hinweis.
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!
Da lag ich ja fast..... ähhh nein ganz daneben mit meiner Annahme auf die Variable bezogen. 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
Jürgen
  Mit Zitat antworten Zitat
Benutzerbild von juergen
juergen

Registriert seit: 10. Jan 2005
Ort: Bönen
1.176 Beiträge
 
Delphi 11 Alexandria
 
#7

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

  Alt 30. Mai 2013, 23:38
@Sir Rufo,

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

Nochmals Danke!
Jürgen
Indes sie forschten, röntgten, filmten, funkten, entstand von selbst die köstlichste Erfindung: der Umweg als die kürzeste Verbindung zwischen zwei Punkten. (Erich Kästner)
  Mit Zitat antworten Zitat
Benutzerbild von BUG
BUG

Registriert seit: 4. Dez 2003
Ort: Cottbus
2.094 Beiträge
 
#8

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

  Alt 31. Mai 2013, 02:31
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

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.
Intellekt ist das Verstehen von Wissen. Verstehen ist der wahre Pfad zu Einsicht. Einsicht ist der Schlüssel zu allem.

Geändert von BUG (31. Mai 2013 um 02:37 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

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 02:36 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