Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Fluent Design und Records (https://www.delphipraxis.net/201758-fluent-design-und-records.html)

hschmid67 23. Aug 2019 05:31

Fluent Design und Records
 
Liebe Delphi-Gemeinde,

ich mag im Design meiner Objekte das Pattern des Fluent Design sehr gerne und gestalte meine Methoden deshalb gerne damit. Ich meine damit also etwa ein solches Gebilde:

Delphi-Quellcode:
Result := CreateSomeInterface.DoSomething('withParameter').DoSomethingOther.ConvertEverythingToString;


Das ist an manchen Stellen sehr praktisch und macht den Code in meinen Augen etwas schöner, vor allem, wenn man mit Interfaces statt mit Objekten arbeitet.

Nun frage ich mich, ob man das auch mit Records machen kann, bzw. ob das sinnvoll ist.

Angenommen ich habe folgendes:

Delphi-Quellcode:
type
  Tfluent = record
    variable: string;
    function DoSomething(const mParameter: string): Tfluent;
  end;

function Tfluent.DoSomething(const mParameter: string): Tfluent;
begin
  variable := variable + mParameter;
  Result := Self;
end;
Soweit ich das verstehe, würde das
Delphi-Quellcode:
Result := Self;
bei Records immer dazu führen, dass der komplette Record kopiert wird, da eine Übergabe bei Records ja "als Wert" und nicht "als Zeiger" funktioniert.

Damit wäre das Fluent Design bei Records nicht sinnvoll und würde ständig eine Kopiererei der Daten verursachen. Ist das so, oder gibt es einen Trick? Wie macht ihr das?

Ich fände die Records deshalb sehr schön, weil man z.B. mit Operator Overloading sich dann eine Methode "ConvertEverthingToString" sparen könnte, was meines Wissens mit Objekten/Interfaces bislang noch nicht geht.

Viele Grüße
Harald

Redeemer 23. Aug 2019 06:51

AW: Fluent Design und Records
 
Man kann einen Record erstellen, der als einzige Methoden Konstruktoren hat. Das ist natürlich Quatsch, würde aber vielleicht funktionieren. Ich setze schon mal einen Helm auf, weil ich gleich gesteinigt werde.

uligerhardt 23. Aug 2019 07:00

AW: Fluent Design und Records
 
Du könntest jeweils einen Zeiger auf Self zurückgeben. Also Result := @Self (mit Klammeraffe).
Da Delphi erlaubt, beim Dereferenzieren den ^ wegzulassen, könnten die Aufrufe sogar identisch ausschauen.
(Wobei ich dieses Feature persönlich verabscheue. Aber die Geschmäcker sind verschieden.)

Mavarik 23. Aug 2019 09:46

AW: Fluent Design und Records
 
Eigentlich hat uligerhardt schon alles gesagt...

Ich würde jedoch das ^ nie weglassen... Einfach um den Ursprung besser zu verstehen.

uligerhardt 23. Aug 2019 09:51

AW: Fluent Design und Records
 
Zitat:

Zitat von Mavarik (Beitrag 1442735)
Ich würde jedoch das ^ nie weglassen... Einfach um den Ursprung besser zu verstehen.

:thumb:

Stevie 23. Aug 2019 11:56

AW: Fluent Design und Records
 
Ihr meint sicher Fluent Interface und nicht Fluent Design.

hschmid67 23. Aug 2019 12:52

AW: Fluent Design und Records
 
Zitat:

Ihr meint sicher Fluent Interface und nicht Fluent Design.
Oh, natürlich meinte ich Fluent Interface :oops: - Danke für die Klarstellung.

Und vielen Dank auch an alle, die sich bisher beteiligt hatten. Hmmm, das mit der Adresse und dem ^ ist eine Lösung, aber irgendwie mag ich solche Operatoren in meinem normalen Code nicht - das sieht für mich immer nach Eingriff auf unterster Ebene aus. Und das ist in einer Bibliothek ok, aber für den täglichen Gebrauch... Ob ich dann wohl doch lieber bei den Interfaces bleibe?

Viele Grüße
Harald

uligerhardt 24. Aug 2019 12:51

AW: Fluent Design und Records
 
Zitat:

Zitat von hschmid67 (Beitrag 1442783)
Hmmm, das mit der Adresse und dem ^ ist eine Lösung, aber irgendwie mag ich solche Operatoren in meinem normalen Code nicht - das sieht für mich immer nach Eingriff auf unterster Ebene aus. Und das ist in einer Bibliothek ok, aber für den täglichen Gebrauch... Ob ich dann wohl doch lieber bei den Interfaces bleibe?

Mit dem Haken siehst du halt, dass du dereferenzierst. Bei Variablen von Klassen- oder Interfacetypen dereferenzierst du genau so, nur ist es versteckt. De facto ist die zweite Variante also sogar weniger klar. :wink: (Vom erhöhten Overhead (dynamische Allozierung, virtuelle Methodenaufrufe) ganz zu schweigen.)

hschmid67 24. Aug 2019 14:36

AW: Fluent Design und Records
 
Hallo Zusammen,

jetzt habe ich den Vorschlag von uligerhardt mal ausprobiert, aber irgendwas scheine ich nicht richtig zu verstehen. Könnt Ihr mir nochmal weiterhelfen?

Also, ich hab mal folgende Unit gemacht:

Delphi-Quellcode:
unit hs.str2;

interface

type
  ThsStr2 = record
    FStr: string;
    function AsString: string;
    function Init(const mString: string): ThsStr2;
  end;

implementation

uses
  System.SysUtils;

{ ThsStr2 }

function ThsStr2.AsString: string;
begin
  Result := FStr;
end;

function ThsStr2.Init(const mString: string): ThsStr2;
begin
  FStr := mString;
//  Result := @Self; // so war der Vorschlag, aber das kompiliert gar nicht "Pointer und ThsStr2"
  Result := ThsStr2(@Self); // so kompiliert es, aber wirft eine AV bei der Ausführung
end;

end.
Testen wollte ich das Ganze so:

Delphi-Quellcode:
procedure Test;
var
  lStr: ThsStr2;
  lDummy: string;
begin
  lDummy := lStr.Init('dummy').AsString;
end;
Aber eben beim Init bekomme ich eine AV. Was mache ich falsch - oder wo ist mein Denkfehler?

Viele Grüße
Harald

uligerhardt 24. Aug 2019 14:42

AW: Fluent Design und Records
 
Sorry, ich hab's etwas kurz erklärt. :oops:
Du musst einen Zeigertyp zurückgeben, also etwas so:
Delphi-Quellcode:
type
  PhsStr2 = ^ThsStr2; // Zeiger auf ThsStr2
  ThsStr2 = record
    FStr: string;
    function AsString: string;
    function Init(const mString: string): PhsStr2; // <== Beachte den Rückgabetyp!
  end;

hschmid67 24. Aug 2019 14:59

AW: Fluent Design und Records
 
Super!

Vielen, vielen Dank. Jetzt hab ich es verstanden.

Hier noch mein vollständiges Beispiel, das jetzt funktioniert (natürlich nur im Anfangsstadium):

Delphi-Quellcode:
unit hs.str2;

interface

type
  PhsStr2 = ^ThsStr2;
  ThsStr2 = record
    FStr: string;
    function Init(const mString: string): PhsStr2;
    class operator implicit(const aValue: ThsStr2): string;
    class operator implicit(const aValue: string): ThsStr2;
  end;

implementation

uses
  System.SysUtils;

{ ThsStr2 }

class operator ThsStr2.implicit(const aValue: ThsStr2): string;
begin
  Result := aValue.FStr;
end;

class operator ThsStr2.implicit(const aValue: string): ThsStr2;
begin
  Result.FStr := aValue;
end;

function ThsStr2.Init(const mString: string): PhsStr2;
begin
  FStr := mString;
  Result := @Self;
end;

end.
Und der Aufruf läuft jetzt erfolgreich:

Delphi-Quellcode:
procedure Test;
var
  lStr: ThsStr2;
  lDummy: string;
begin
  lDummy := lStr.Init('dummy');
end;
Genau das, was ich gesucht hatte - und in meinen Augen dann richig schöner Code, denn man kann das Ganze ja beliebig erweitern, etwa


Delphi-Quellcode:
lDummy := lStr.Init('dummy').CopyFromChar('-').CopyToChar('#');


Das möchte ich nämlich gerade machen: All meine String-Funktionen in einen Aufruf, in ein Objekt/einen Record zu packen, ohne globale Proceduren oder besonderes Speichermanagement (zumindest nicht sichtbar).

Jetzt wäre es nur noch schön, wenn man es so aufrufen könnte:


Delphi-Quellcode:
lDummy := lStr('dummy').CopyFromChar('-').CopyToChar('#');


also ohne das Init, aber das geht wohl nicht... :wink:

Vielen Dank und viele Grüße
Harald

uligerhardt 24. Aug 2019 15:06

AW: Fluent Design und Records
 
Schaut gut aus. Bzgl. des letzten Satzes: Du könntest dem Record einen Konstruktor oder eine Factory-Funktion verpassen. Dann solltest du so etwas schreiben können:
Delphi-Quellcode:
lDummy := ThsStr2.Create('dummy').CopyFromChar('-').CopyToChar('#'); // Mit Record-Konstruktor
// oder
lDummy := MakehsStr2('dummy').CopyFromChar('-').CopyToChar('#'); // Mit Factory-Funktion

hschmid67 24. Aug 2019 15:17

AW: Fluent Design und Records
 
Hallo, auch hierfür nochmal danke. :wink:
Klar, das, was ich mit Objekten und Interfaces machen kann, also eine Factory-Methode, das kann ich auch mit einem Record machen - hätte ich auch selbst drauf kommen können.

Verblüffend, was man mit Delphi doch auch alles machen kann, wenn man nur weiß, wie!

Ich werde es jetzt also so machen (doch eine globale Funktion, aber die eine passt dann schon):

function hsStr(const mValue: string = ''): ThsStr2;

und dann sieht der Aufruf tatsächlich so aus - und ich kann es mit oder ohne Initialisierungsstring aufrufen:

Delphi-Quellcode:
lDummy := hsStr('dummy').CopyFromChar('-').CopyToChar('#');


Vielen Dank und herzliche Grüße

Harald

uligerhardt 24. Aug 2019 15:19

AW: Fluent Design und Records
 
:thumb:

hschmid67 24. Aug 2019 16:06

AW: Fluent Design und Records
 
noch zwei Ergänzungen:

Ich war vorhin wohl doch zu voreilig: Es funktioniert nicht ganz ohne Dereferenzierung, zumindest nicht die implizite Zuweisung. Will ich das Ergebnis nach einem Implicit-Operator direkt einem String zuweisen, dann geht es nur so:

Delphi-Quellcode:
lDummy := hsStr('dummy').CopyFromChar('-').CopyToChar('#')^; // funktioniert mit dem abschließenden ^
lDummy := hsStr('dummy').CopyFromChar('-').CopyToChar('#'); // funktioniert NICHT ohne das abschließende ^
hmmm :(

Allerdings habe ich inzwischen auch die Geschwindigkeit der beiden Konstrukte getestet, also einmal mit einem Objekt/Interface und einmal mit einem Record und die Record-Variante ist bei sonst völlig identischem Code fast doppelt so schnell.

Viele Grüße
Harald

uligerhardt 24. Aug 2019 16:17

AW: Fluent Design und Records
 
Zitat:

Zitat von hschmid67 (Beitrag 1442968)
Ich war vorhin wohl doch zu voreilig: Es funktioniert nicht ganz ohne Dereferenzierung, zumindest nicht die implizite Zuweisung. Will ich das Ergebnis nach einem Implicit-Operator direkt einem String zuweisen, dann geht es nur so:

Delphi-Quellcode:
lDummy := hsStr('dummy').CopyFromChar('-').CopyToChar('#')^; // funktioniert mit dem abschließenden ^
lDummy := hsStr('dummy').CopyFromChar('-').CopyToChar('#'); // funktioniert NICHT ohne das abschließende ^
hmmm :(

Dass man den ^ überhaupt manchmal weglassen darf, ist eh schon eine Krücke. Wo es dann zu uneindeutig wird, ist es halt ganz verboten.

Zitat:

Zitat von hschmid67 (Beitrag 1442968)
Allerdings habe ich inzwischen auch die Geschwindigkeit der beiden Konstrukte getestet, also einmal mit einem Objekt/Interface und einmal mit einem Record und die Record-Variante ist bei sonst völlig identischem Code fast doppelt so schnell.

Jo. Ich vermute mal, weil keine überflüssigen Speichereservierungen vorgenommen werden. Bei einem Interface kämen dann auch noch die virtuelle Methodenauflösung hinzu, wenn ich mich nicht täusche.

peterbelow 25. Aug 2019 16:09

AW: Fluent Design und Records
 
Zitat:

Zitat von hschmid67 (Beitrag 1442702)
Liebe Delphi-Gemeinde,

ich mag im Design meiner Objekte das Pattern des Fluent Design sehr gerne und gestalte meine Methoden deshalb gerne damit. Ich meine damit also etwa ein solches Gebilde:

Delphi-Quellcode:
Result := CreateSomeInterface.DoSomething('withParameter').DoSomethingOther.ConvertEverythingToString;


Das ist an manchen Stellen sehr praktisch und macht den Code in meinen Augen etwas schöner, vor allem, wenn man mit Interfaces statt mit Objekten arbeitet.

Naja, Schönheit liegt im Auge des Betrachters. :wink:

Stell Dir mal vor, Du mußt sowas debuggen, dann ist es nicht mehr so schön...

dummzeuch 25. Aug 2019 17:54

AW: Fluent Design und Records
 
Zitat:

Zitat von peterbelow (Beitrag 1443150)
Zitat:

Zitat von hschmid67 (Beitrag 1442702)
Delphi-Quellcode:
Result := CreateSomeInterface.DoSomething('withParameter').DoSomethingOther.ConvertEverythingToString;
Das ist an manchen Stellen sehr praktisch und macht den Code in meinen Augen etwas schöner, vor allem, wenn man mit Interfaces statt mit Objekten arbeitet.

Naja, Schönheit liegt im Auge des Betrachters. :wink:

Stell Dir mal vor, Du mußt sowas debuggen, dann ist es nicht mehr so schön...

Einfach vernuenftig formatieren ...
Delphi-Quellcode:
Result := CreateSomeInterface
  .DoSomething('withParameter')
  .DoSomethingOther
  .ConvertEverythingTo
... und schon ist das kein Problem mehr. Und lesbarer ist es (IMHO) auch.
(Ob der OP das dann allerdings immernoch "schön" findet?).

HolgerX 25. Aug 2019 19:03

AW: Fluent Design und Records
 
Hmm..

Zitat:

Zitat von dummzeuch (Beitrag 1443161)
Zitat:

Zitat von peterbelow (Beitrag 1443150)
Zitat:

Zitat von hschmid67 (Beitrag 1442702)
Delphi-Quellcode:
Result := CreateSomeInterface.DoSomething('withParameter').DoSomethingOther.ConvertEverythingToString;
Das ist an manchen Stellen sehr praktisch und macht den Code in meinen Augen etwas schöner, vor allem, wenn man mit Interfaces statt mit Objekten arbeitet.

Naja, Schönheit liegt im Auge des Betrachters. :wink:

Stell Dir mal vor, Du mußt sowas debuggen, dann ist es nicht mehr so schön...

Einfach vernuenftig formatieren ...
Delphi-Quellcode:
Result := CreateSomeInterface
  .DoSomething('withParameter')
  .DoSomethingOther
  .ConvertEverythingTo
... und schon ist das kein Problem mehr. Und lesbarer ist es (IMHO) auch.
(Ob der OP das dann allerdings immernoch "schön" findet?).

So etwas ist immer gefährlich....

Was ist, wenn es beim '.DoSomething('withParameter')' kracht oder etwas geliefert wird, was in '.DoSomethingOther' zur Exception führt?

Wenn ich das in einer Zeile schreibe, wie kann ich dann im Debugger erkennen, wo es geknallt hat?

(Nur so meine Meinung ;) )

stahli 25. Aug 2019 19:54

AW: Fluent Design und Records
 
Mal eine Frage, was Ihr grundsätzlich von einer solchen Darstellung haltet (mal völlig unabhängig von Delphi und anderen realen Sprachen).
Es geht mir nur um die reine Lesbarkeit und Wünsche diesbezüglich.

Das wäre ja Delphi ohne irgendwelche Klimmzüge:
Code:
  Result := CreateSomeInterface;
  Result.DoSomething('withParameter');
  Result.DoSomethingOther;
  Result.ConvertEverythingTo;
Warum will man das anders darstellen?
Um nicht mehrfach "Result" zu schreiben?

---

Ok, wenn man jetzt (rein hypothetisch!) dem IDE-Editor folgendes beibringen könnte:
"Wenn in den folgenden Zeilen das gleiche erste Wort steht und dann ein Punkt folgt, dann lasse das erste Wort in der Darstellung weg und rücke alles ab dem Punkt ein!"
dann würde das so aussehen:
Code:
  Result := CreateSomeInterface;
         .DoSomething('withParameter');
         .DoSomethingOther;
         .ConvertEverythingTo;
---

Da aber dann der Editor abweichenden Code darstellen müsste als der, den der Compiler erhält, müsste man das etwas anders aufziehen:
Man müsste dem Compiler sagen: "Wenn eine Anweisung mit einem Punkt beginnt, dann denke Dir mal, dass davor die Variable steht, der in der vorherigen Anweisung etwas zugewiesen wurde."
Die IDE könnte einfach die Anweisung ab dem Punkt einrücken.
Code:
  Result := CreateSomeInterface;
         .DoSomething('withParameter');
         .DoSomethingOther;
         .ConvertEverythingTo;
Das sähe also aus wie bei der vorherigen Überlegung, aber der Code wäre identisch zur Darstellung.
Das hätte den Haken, dass man letztlich nur in Verbindung mit der Einrückung klar erkennt, was dort gemeint ist.

---

Gut, schauen wir uns mal with an:
Code:
  Result := CreateSomeInterface;
  with Result do
  begin
    DoSomething('withParameter');
    DoSomethingOther;
    ConvertEverythingTo;
  end;
Eigentlich ja gar nicht sooo schlecht, allerdings bekannter Maßen mit einem recht hohen Verwechslungsrisiko und die Umschließung mit begin..end ist auch etwas nervig.

---

Mich würde das zu folgenden gewünschten Sprachfeature führen: "use"
Code:
  use Result := CreateSomeInterface;
             .DoSomething('withParameter');
             .DoSomethingOther;
             .ConvertEverythingTo;
             Report(.Text);
  Report('use gilt nicht mehr');
"use" würde with entsprechen und so lange gelten, wie folgende Anweisungen Bezeichner benutzen, die mit einem Punkt anfangen.
Dann wäre auch noch die Report-Anweisung möglich.
Sobald eine Anweisung nicht nicht mehr auf Result bezieht (also kein Bezeichner mit einem . anfängt) ist der use-Bezug hinfällig und ungültig.

---

Wenn, dann würde ich mir so etwas wünschen.
Das wäre nachvollziehbar und einfach ein besseres "with".

Alle Klassen umzubauen um Anweisungen mit einem Punkt starten zu können wäre mir zu aufwendig und bezüglich der Debugbarkeit halte ich das auch nicht für hilfreich.
Außerdem sollten Funktionen für die Logik da sein und nicht für das Code-Styling.

Wenn ich eine Methode TPerson.SetIsNice(True) habe, dann sollte die möglicherweise für die Validierung ein Accept zurück geben und nicht die Klasse.

Also ich brauche ein fluent interface eher nicht und würde eher ein anderes Sprachfeature wünschen, um Codekürzungen zu erreichen. Da wäre ich dann dabei.

Schokohase 25. Aug 2019 20:13

AW: Fluent Design und Records
 
@stahli

Das Besondere am Fluent Interface ist kein Ersatz für
Delphi-Quellcode:
Result := CreateSomeInterface;
Result.DoSomething('withParameter');
Result.DoSomethingOther;
Result.ConvertEverythingTo;
sondern das hier
Delphi-Quellcode:
ResultA := CreateSomeInterface; // IFoo
ResultB := ResultA.DoSomething('withParameter'); // IBar
ResultC := ResultB.DoSomethingOther; // IOther
ResultD := ResultC.ConvertEverythingTo; // string
Natürlich ist es möglich das alle ResultX vom gleichen Typ sind und dann auch auf die gleiche Instanz verweisen, aber sie müssen es eben nicht.

Und die eingebaute Code-Formatierung formatiert das so (wenn man einen Zeilenkommentar anhängt)
Delphi-Quellcode:
Result := CreateSomeInterface() // IFoo
  .DoSomething('withParameter') // IBar
  .SoSomethingOther() // IOther
  .ConvertEverythingTo(); // string

Mavarik 26. Aug 2019 12:44

AW: Fluent Design und Records
 
Es gibt mehrere Gründe den Sourcecode "so" zu schreiben.

Beispiel SQL:

Delphi-Quellcode:
  TCRUDSearch.&For(fPerson).Where('NAME').LIKE(LastName.Trim)
  {}         .begin_Optional(FirstName.Trim <> '')
  {}            ._AND.Where('VORNAME').LIKE(FirstName.Trim)
  {}         .end_Optional.OrderBy('NAME')
  {}         .OrderBy('VORNAME ASC')
  {}         .Limit(100)
  {}         .Start(SearchResult);
Wie üblich die
Delphi-Quellcode:
{}
, damit der Formatter das nicht zerstört.
Nicht nur das ich mit diesem Interface keine Tippfehler mehr im "SQL-Source-Text" habe, ich kann auch mit dem "begin Optional(Statement)" je nach Kondition des Statements einen anderen SQL Code erzeugen.

oder halt sowas:

Delphi-Quellcode:
    TFMXFluentCreator<TDayLBL>.CreateDefault(FDayLBL[i-1],LConfig)
    {}                        .FontSize(FFontSize)
    {}                        .Skip(LConfig.Trim <> '',2).FontColor(LDisplayDay.Color).FontBold(LDisplayDay.Bold)
    {}                        .Text(FDays[i-1])
    {}                        {$IFNDEF NOHints}
    {}                          .Skip((LDisplayDay.Hint = '')).Hint(LDisplayDay.Hint)
    {}                        {$ENDIF}
    {}                        ;
Ich empfehle hierzu mein CodeRage Video. Auch wenn hier leider noch von Fluid und nicht Fluent gesprochen wird. // Danke Stefan hab es in ALLEN Sourcen renamed...

Hier gibe es wie bei meine SQL Creator (begin/end) ein Skip Command mit dem man 1-N der nächsten Fluent-Aufrufe überspringen kann je nach Kondition.
Per LConfig kann ich Aufruf die ich beliebig vordefinieren kann, zusätzlich abrufen.

Wenn man ListBoxItems im Source erzeugen möchte ist das eine fürchterliche Tipperei, besonders durch die TWhateverbezeichner.Value {$SCOPEDENUMS ON}.

Da sieht es doch schon deutlich einfacher aus, wenn man

Delphi-Quellcode:
TFMXFluentCreator<TListBoxItem>.RegisterDefault(Procedure (LBI : TFMXFluentCreator<TListBoxItem>)
  begin
    LBI.Style('listboxitembottomdetail')
       .Height(49)
       .Parent(ListBox1)
       .ItemMore.OnClick(Procedure (Sender : TObject)
          begin
            AViewModel.Edit((Sender as TListBoxItem).TagString);
            Translate.SetFocus;
          end,false);
  end);
schreiben kann..

Grüsse Mavarik :coder:

dummzeuch 26. Aug 2019 14:03

AW: Fluent Design und Records
 
Zitat:

Zitat von HolgerX (Beitrag 1443168)
Hmm..

Zitat:

Zitat von dummzeuch (Beitrag 1443161)
Zitat:

Zitat von peterbelow (Beitrag 1443150)
Stell Dir mal vor, Du mußt sowas debuggen, dann ist es nicht mehr so schön...

Einfach vernuenftig formatieren ...
Delphi-Quellcode:
Result := CreateSomeInterface
  .DoSomething('withParameter')
  .DoSomethingOther
  .ConvertEverythingTo
... und schon ist das kein Problem mehr. Und lesbarer ist es (IMHO) auch.
(Ob der OP das dann allerdings immernoch "schön" findet?).

So etwas ist immer gefährlich....

Was ist, wenn es beim '.DoSomething('withParameter')' kracht oder etwas geliefert wird, was in '.DoSomethingOther' zur Exception führt?

Wenn ich das in einer Zeile schreibe, wie kann ich dann im Debugger erkennen, wo es geknallt hat?

In der Regel ja. Deshalb die Formatierung mit einem Aufruf pro Zeile.
Zum Debuggen wäre natürlich eine Variable einfacher:

Delphi-Quellcode:
Result := CreateSomeInterface;
Result := Result.DoSomething('withParameter');
Result := Result.DoSomethingOther;
Result := Result.ConvertEverythingTo;
Aber das versaut das "schöne" dann komplett. ;-)

(Vorausgesetzt, es ist durchgehend derselbe Interface-Typ, denn wie Schokohase schon schrieb: Das ist nicht notwendigerweise der Fall. Wenn nicht, muesste man auch noch jede Menge Variablen deklarieren, was noch viel mehr Tipperei ist.)

Ich habe einige wenige solcher Konstruktionen, bei denen diese Kaskade einen Funktionsaufruf mit zig Parametern ersetzt hat. Die Lesbarkeit hat es auf jeden Fall verbessert.

Uwe Raabe 26. Aug 2019 14:43

AW: Fluent Design und Records
 
Zitat:

Zitat von dummzeuch (Beitrag 1443315)
Zum Debuggen wäre natürlich eine Variable einfacher

Macht sich auch besser im Watch-Fenster oder bei Ctrl-F7.

Es ist halt immer ein Kompromiss zwischen "schön" bzw. lesbar und leichter zu debuggen.

Zitat:

Zitat von dummzeuch (Beitrag 1443315)
Wenn nicht, muesste man auch noch jede Menge Variablen deklarieren, was noch viel mehr Tipperei ist.

Na ja, unter Rio hält sich das ja noch in Grenzen:
Delphi-Quellcode:
begin
  var ResultA: IFoo := CreateSomeInterface; // IFoo
  var ResultB: IBar := ResultA.DoSomething('withParameter'); // IBar
  var ResultC: IOther := ResultB.DoSomethingOther; // IOther
  var ResultD: string := ResultC.ConvertEverythingTo; // string
end;
Ach ja, früher galt with auch als richtig cool, weil man da soviel Tipperei spart.

stahli 26. Aug 2019 15:03

AW: Fluent Design und Records
 
Solche Zwischenschritte in einzelne Variablen zu speichern stört mich gar nicht. Ich finde, das erhöht sogar die Übersichtlichkeit deutlich.

Das mache ich auch schon mal bei etwas komplizierteren Berechnungen (Winkel o.ä.). Da nehme ich gern in Kauf, mal eine Variable anzulegen.

Einzig, eine ständige Wiederholung der gleichen Variable würde ich u.U. gern vermeiden, wenn es dafür eine abgekürzte Darstellung/Schreibweise gäbe (Alternative zum with).
Meine Klassen würde ich dafür aber nicht umbauen.


In Bezug auf "Tipparbeit vermeiden" fallen mir da ganz andere Themen ein, die ich wirklich wichtig fände und wo die IDE wirklich keine Hilfe ist.

Mavarik 26. Aug 2019 15:39

AW: Fluent Design und Records
 
Naja sagen wir mal so...

Für ein Interface, Klasse oder Record das man so verwendet/erstellt bietet es sich natürlich an, einen Unittest zu schreiben, dann braucht man i.d.R. auch nicht debuggen oder eher selten.

Mavark :coder:

Uwe Raabe 26. Aug 2019 15:53

AW: Fluent Design und Records
 
Zitat:

Zitat von mavarik (Beitrag 1443336)
bietet es sich natürlich an, einen unittest zu schreiben, dann braucht man i.d.r. Auch nicht debuggen oder eher selten.

:lol:


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:52 Uhr.

Powered by vBulletin® Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf