Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Über den Umgang mit vielen optionalen Parametern (https://www.delphipraxis.net/149890-ueber-den-umgang-mit-vielen-optionalen-parametern.html)

Meflin 1. Apr 2010 19:56


Über den Umgang mit vielen optionalen Parametern
 
Thread zur Steigerung der Codequalität Nr. 2....

Ich denke fast jeder kennt das Problem... ich habe hier z.B. eine Funktion mit 4 optionalen Parametern (von denen 3 auch noch typgleich sind - Zahlen frei erfunden ;) ). Mit dem klassischen Versuch:
Delphi-Quellcode:
function foo(a): z;
function foo(a, b): z;
function foo(a, b, c): z;
und der Weiterleitung der parameterarmen Funktionsrufen an die parameterreichen kommt man da schnell nicht mehr weiter - zumindest nicht schön.

Also habe ich mir überlegt... man erstellt sich einen Record mit einem Feld pro Parameter.
Delphi-Quellcode:
TParams = record
  paramAValue: integer;
  paramBValue: integer;
  paramCValue: integer;
  function paramA(i: Integer): TParams;
  ...
end;

TParams.paramA(i: Integer): TParams;
begin
  Result := Clone(self);
  Result.paramAValue := i;
end;
Verwenden könnte man das ganze dann so:
Delphi-Quellcode:
var
  params: TParams;
begin
  result := foo(params.paramA(1).paramF(120));
Allerdings ist das auch noch blöd, wegen der zwingend nötigen params-Variable. Die will man ja möglichst auch noch loswerden.

Ideal wäre also sowas (WAS LEIDER NICHT ZU GEHEN SCHEINT)
Delphi-Quellcode:
TParams = class
  paramAValue: Integer;
  ...
  function paramA(i: Integer): TParams;
  ...
  class function paramA(i: Integer): TParams;
Da könnte man dann auch schön beim Aufruf der Klassenmethoden die Felder mit default-werten belegen (denn das ist dann ja eindeutig die Stelle wo ein neues Objekt erzeugt wird). Verwenden könnte man es dann einfach so:
Delphi-Quellcode:
result := foo(TParams.paramA(5));
Nunja... das scheitert schon allein daran, dass man keine gleichnahmigen class/instance-Methods anlegen kann :( (Smalltalk kann das :tongue: )

Soo... was haltet ihr allgemein von der Idee? Habt ihr irgendwelche anderen / besseren / zielführenderen?

Neutral General 1. Apr 2010 19:59

Re: Umgang mit vielen optionalen Parametern
 
Dir ist schon klar, dass folgendes geht:

Delphi-Quellcode:
function foo(a: Integer; b: Integer = 0; c: Integer = 0): z;

foo(1);
foo(1,2);
foo(1,2,3);
?

Meflin 1. Apr 2010 20:00

Re: Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von Neutral General
Delphi-Quellcode:
function foo(a: Integer; b: Integer = 0; c: Integer = 0): z;

Und wenn ich jetzt c angeben will, b aber nicht?

Neutral General 1. Apr 2010 20:01

Re: Über den Umgang mit vielen optionalen Parametern
 
Dann musst du in den sauren Apfel beißen und B doch angeben :-P
Ich setze häufig genutzte Parameter dann eben ganz weit links in die Liste. Die am seltensten genutzten optionalen Parameter kommen dann weiter nach rechts.

Edit: Hab mir deinen Ansatz nicht genau angeschaut. Sieht aber nach Overkill aus.

wicht 1. Apr 2010 20:07

Re: Über den Umgang mit vielen optionalen Parametern
 
So mache ich es auch meistens, also wie Neutral General es sagte.
Dieses Konzept hier mit dem Record wird so ähnlich ja auch an vielen Stellen in Zusammenhang mit der WinAPI zum Beispiel angewendet - ist wohl etwas weniger Performant, das dann auch noch mit Klassen zu machen, aber...

Meflin 1. Apr 2010 20:08

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von Neutral General
Edit: Hab mir deinen Ansatz nicht genau angeschaut. Sieht aber nach Overkill aus.

Es geht hier um ~5-45 Methoden die dieses Parameterset dann verwenden könnten. Für die ich sonst jeweils ~5-10 Überladungen erstellen müsste. Also nein, nicht wirklich Overkill...

alzaimar 1. Apr 2010 20:16

Re: Über den Umgang mit vielen optionalen Parametern
 
Wenn Du so viele optionale Parameter hast, dann macht die Funktion aber mehr als sie sollte. Wieso bricht du dir denn einen ab und versuchst auf Deibel komm raus, so viel Funktionalität in eine Funktion zu packen?

Ich würde mir mal Gedanken machen, ob das der richtige Weg ist.

himitsu 1. Apr 2010 20:22

Re: Über den Umgang mit vielen optionalen Parametern
 
Offiziell würde man für sowas wohl eine Parameter-Klasse erstellen
oder gleich die Funktion in eine eigene Klasse verfrachten und dann die Parameter extra behandeln (also Funktion ohne direkte Parameter).

Delphi-Quellcode:
var params: TParams;
result := foo(params.clear.paramA(1).paramF(120));
// oder
result := foo(TParams.Create.paramA(1).paramF(120));
Ansonsten vergiß nicht, daß du eventuell die ungenutzen Parameter auch so initialisieren mußt, so daß sie als ungenutzt erkennbar sind oder daß sie wenigstens mit einen Standardwert belegt sind.

In Clear oder Create würden dann erstmal alle Parameter initialisiert.
(Bei Strings wäre das ja kein Problem, da sie automatisch von Delphi mit '' initialisiert werden,
aber deine Beispielinteger hätten so, mit großer Wahrscheinlichkeit, undefinierte Zustände.

Meflin 1. Apr 2010 20:23

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von alzaimar
Wenn Du so viele optionale Parameter hast, dann macht die Funktion aber mehr als sie sollte. Wieso bricht du dir denn einen ab und versuchst auf Deibel komm raus, so viel Funktionalität in eine Funktion zu packen?
Ich würde mir mal Gedanken machen, ob das der richtige Weg ist.

Ich mache mir ja grade Gedanken, weil es mir eben mit Michael's Methode absolut nicht mehr zielführend erscheint ;)

Die Funktionen setzen im Endeffekt nur Requests an einen Webservice zusammen (so richtig viel kann ich nich dazu sagen... quasi-NDA) - machen also nicht sehr viel und eine Aufspaltung in weitere Funktionen wäre sinnlos. Der Service bietet halt nunmal diese Optionen an, die ich irgendwie in meinem Wrapper nach außen tragen muss.

Und genau darum geht es ja gerade: Wie.

Ein völlig gegenläufiges Konzept wäre es z.B., pro Funktion/Request-Typ eine Klasse anzulegen. Hat halt auch Vor- und Nachteile...
Zitat:

Zitat von himitsu
Ansonsten vergiß nicht, daß du eventuell die ungenutzen Parameter auch so initialisieren mußt, daß sie als ungenutzt erkennbar sind oder daß sie wenigstens mit einen Standardwert belegt sind.

Alles, was nicht verwendet wird, könnte glaube ich problemlos mit einem Default-Wert belegt werden (also im Grunde bei Create).

alzaimar 1. Apr 2010 20:31

Re: Über den Umgang mit vielen optionalen Parametern
 
Ok, dann ist das Konzept der Parameterstrukturen zu empfehlen, und zwar mit definierten Default/ Don't Care - Werten. Hier könnten z.B. Variants zum Einsatz kommen, mit "Null" als Defaultwert.

Khabarakh 1. Apr 2010 22:52

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von Meflin
Allerdings ist das auch noch blöd, wegen der zwingend nötigen params-Variable. Die will man ja möglichst auch noch loswerden.

Generics machen's möglich... sogar, wenn man sie direkt gar nicht einsetzt :mrgreen: .
Delphi-Quellcode:
foo(Default(TParams).paramA(1).paramF(120))
Viel schöner wäre natürlich, wenn sie mal ein wenig bei Prism plündern würden :) .
Delphi-Quellcode:
Foo(new Params(A = 1, F = 120));
Und für etwas ältere Versionen: Wie wäre es mit einem einfachen
Delphi-Quellcode:
foo(TParams.GetDefault.paramA(1)...
:?:


Zitat:

Zitat von Meflin
Zitat:

Zitat von Neutral General
Delphi-Quellcode:
function foo(a: Integer; b: Integer = 0; c: Integer = 0): z;

Und wenn ich jetzt c angeben will, b aber nicht?

Deswegen setzen übrigens viele neuere Sprachen auf optionale und benannte Parameter :) .
Code:
foo(1, c: 2);

Zitat:

Zitat von himitsu
oder gleich die Funktion in eine eigene Klasse verfrachten und dann die Parameter extra behandeln (also Funktion ohne direkte Parameter).

Schenkt man Microsofts Framework Design Guidelines Glauben, stellt das die Otto-Normalentwickler-freundlichste Lösung dar - "Component-Oriented Design" bzw. "Create-Set-Call Pattern". Für den Implementeur zwar manchmal etwas mehr Arbeit, aber mit solchen APIs scheinen Leute am besten klar zu kommen, wenn sie mit Code Insight darin herumstochern. Eine beiliegende Hilfe ist schließlich höchstens etwas fürs stille Örtchen ;P .

Meflin 2. Apr 2010 02:13

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von Khabarakh
Und für etwas ältere Versionen: Wie wäre es mit einem einfachen
Delphi-Quellcode:
foo(TParams.GetDefault.paramA(1)...
:?:

Das wirds jetzt wohl werden :thumb: Da komme ich tatsächlich ohne Überladung aus und das ganze verwendet sich wunderbarst flexibel (und auch die Einführung neuer Parameter ist kein Problem) :)
Zitat:

Zitat von Khabarakh
Schenkt man Microsofts Framework Design Guidelines Glauben, stellt das die Otto-Normalentwickler-freundlichste Lösung dar - "Component-Oriented Design" bzw. "Create-Set-Call Pattern". Für den Implementeur zwar manchmal etwas mehr Arbeit, aber mit solchen APIs scheinen Leute am besten klar zu kommen, wenn sie mit Code Insight darin herumstochern. Eine beiliegende Hilfe ist schließlich höchstens etwas fürs stille Örtchen ;P .

Hm. Create-Set-Call kann man ja trotzdem noch machen. Es geht ja weiterhin
Delphi-Quellcode:
var
  params: TParams;
begin
  params := TParams.Create;
  params.a := foo;
  params.b := bar;
  foo(params);
  // gleichbedeutend mit
  foo(TParams.getDefault.setA(foo).setB(bar));
end;
Aber ich bin mir nicht sicher ob das gemeint war :mrgreen:

alzaimar 2. Apr 2010 09:06

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von Meflin
Delphi-Quellcode:
  foo(TParams.getDefault.setA(foo).setB(bar));

Geht es nicht auch darum, den Code irgendwie lesbar zu gestalten? Das ist doch grauslig, was Du da anstellst. Sieht zwar 'pfiffig' aus, aber lesbar geht anders.

Ich frage mich auch, wie eine Funktion aussehen soll, die einerseits eine Latte von Parametern akzeptiert, von denen alle optional sind, und andererseits so einfach gestrickt ist, das man sie nicht in mehrere Funktionen aufbrechen kann. Aber gut, vielleicht ist es auch nur eine Design- bzw. 'Best practice' Frage.

himitsu 2. Apr 2010 09:12

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Allerdings ist das auch noch blöd, wegen der zwingend nötigen params-Variable. Die will man ja möglichst auch noch loswerden.
Du kannst einem Record auch eine statische Class-Funktion, bzw. einen Constuctor verpassen.

[add]
Delphi-Quellcode:
type
  TParams = record
    //class function Create: TParams; static;
    constructor Create;
    function Default: TParams;
    function paramA(i: Integer): TParams;
    function paramB(i: Integer): TParams;
    function ParamList: TParams;
  private
    paramAValue: Integer;
    paramBValue: Integer;
  end;

constructor TParams.Create;
begin
  Default;
end;

function TParams.Default: TParams;
begin
  Finalize(Self);
  FillChar(@Self, SizeOf(Self), 0);
  Result := Self;
end;

function TParams.paramA(i: Integer): TParams;
begin
  paramAValue := i;
  Result := Self;
end;

function TParams.paramB(i: Integer): TParams;
begin
  paramBValue := i;
  Result := Self;
end;

function TParams.ParamList: TParams;
begin
  Result := Self;
end;
Oder das Ganze als Interface.
Als Interface wäre es Speichersparender.
Also Objekt ginge zwar auch, aber da darf man dann das Freigeben nicht vergessen, welches bei einigen Aufrufvarianten unmöglich/schwer zu realisieren ist.

Delphi-Quellcode:
foo(TParams.Create.paramA(1).paramB(9));

var Params: TParams;
Params.Default; // oder Params := TParams.Create;
Params.paramA(1);
Params.paramB(9);
foo(Params);

with TParams.Create do begin
  paramA(1);
  paramB(9);
  foo(ParamList);
end;

with TParams.Create do begin
  paramA(1);
  //foo(ParamList.paramB(9));
  foo(paramB(9));
end;

Meflin 2. Apr 2010 09:58

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von alzaimar
Das ist doch grauslig, was Du da anstellst. Sieht zwar 'pfiffig' aus, aber lesbar geht anders.

Welcher Teil genau? Schonmal LINQ benutzt? Schonmal jQuery benutzt? So unüblich ist diese Schreibweise nun auch wieder nicht... (außer der getDefault-Teil. Der ist unschön. Lässt sich aber vermutlich nicht verweiden...). Da gehts ja auch so:
Code:
result := list.select(foo).orderBy(bar).thenBy(bar2).intersect(wuppdi);
und nu?

Zitat:

Ich frage mich auch, wie eine Funktion aussehen soll, die einerseits eine Latte von Parametern akzeptiert, von denen alle optional sind, und andererseits so einfach gestrickt ist, das man sie nicht in mehrere Funktionen aufbrechen kann. Aber gut, vielleicht ist es auch nur eine Design- bzw. 'Best practice' Frage.
Stelle dir eine Funktion vor. Man kann eine Sortierung spezifizieren (enum), ein Offset, ein Limit (jeweils int), einen string und zwei Bools (Verhaltensbeeinflussung). Alle diese Parameter sind optional. Zwingend dazu kommt dann noch eine Id (Int). Im Ergebnis der Funktion kommen alle Parameter vor, auch die nicht verwendeten (da nicht spezifiziert sowieso in jedem Fall gleich default bedeutet und es mehr aufwand wäre, auch noch verschiedene Queries zu generieren wenn dies garnicht nötig wäre). Achso, und auf neue Parameter muss man natürlich auch stets vorbereitet sein.

Your try.

Zitat:

Also Objekt ginge zwar auch, aber da darf man dann das Freigeben nicht vergessen, welches bei einigen Aufrufvarianten unmöglich/schwer zu realisieren ist.
Lokale Variablen sollten aber doch (egal ob nun "anonym" oder nicht) am Funktionsende ohne zutun weggeräumt werden. Oder nicht :gruebel:

BUG 2. Apr 2010 10:09

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von Meflin
Lokale Variablen sollten aber doch (egal ob nun "anonym" oder nicht) am Funktionsende ohne zutun weggeräumt werden. Oder nicht :gruebel:

Delphi (win32) hat keinen Garbage Collector, egal wie sehr du ihn dir wünschst :mrgreen:
Taucht ja in einigen deiner Posts auf.

Das heißt alle Objekte (abgesehen von welchen in Interface-Typen), die du erstellst, musst du auch selbst wegräumen.

Meflin 2. Apr 2010 10:16

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von BUG
Delphi (win32) hat keinen Garbage Collector, egal wie sehr du ihn dir wünschst :mrgreen:

Aber... Es... Ich... http://www.fnse.de/S01/0R2.gif

Das hat man nun davon. Hat man die bessere Welt erstmal kennengelernt, gefällts einem in der anderen nicht mehr :stupid:

himitsu 2. Apr 2010 10:18

Re: Über den Umgang mit vielen optionalen Parametern
 
Interfaces und Records, sowie dyn. Arrays und String räumen sich selber auf, bzw. Delphi macht dieses für einen.

Pointer und Objekte muß man manuell aufräumen


seit D2006: mit einem Record geht es einfach
und mit Interfaces isses Ergebnis "schöner"

Khabarakh 2. Apr 2010 16:50

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von Meflin
Create-Set-Call kann man ja trotzdem noch machen. [...]
Aber ich bin mir nicht sicher ob das gemeint war :mrgreen:

Es geht wirklich nur um den Unterschied, die Funktion dann direkt TParams zuzuordnen (und für den Reocrd oder dann wahrscheinlich Klasse noch vielleicht nen etwas hübscheren Namen zu finden :) ). Soll so anscheinend einfacher bedienbar sein, aber wir fallen wahrscheinlich gar nicht mehr unter die Zielgruppe ;) .

Zitat:

Zitat von himitsu
Du kannst einem Record auch eine statische Class-Funktion, bzw. einen Constuctor verpassen.

Parameterlose Konstruktoren gibt es bei Records (zurecht) nicht.

Zitat:

Zitat von alzaimar
Geht es nicht auch darum, den Code irgendwie lesbar zu gestalten? Das ist doch grauslig, was Du da anstellst. Sieht zwar 'pfiffig' aus, aber lesbar geht anders.

Wenn es ums Initialisieren von Objekten geht, ist mir Fluent Syntax auch nicht geheuer - deswegen die Bitte um Prisms Object Initializers :stupid: . Sowas wie
Code:
new Bar()
.AddItem(1)
.AddItem(2)
.WithOptions()
    .SetCached(true)
.EndOptions()
ist mir dann doch etwas zu "tricky" (und vor allem zu blöd zum Implementieren). Dann lieber
Code:
new Bar {
    Items = { 1, 2 },
    Options = new Options(cached: true)
}
Zwischen new TParams() und TParams.GetDefault sehe ich allerdings keinen großartigen Unterschied :| . In Ruby könnten wir es "gimme!" nennen :mrgreen: .

himitsu 2. Apr 2010 16:55

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von Khabarakh
Parameterlose Konstruktoren gibt es bei Records (zurecht) nicht.

OK, aber die parameterlose Static-Function sollte dennoch gehn. :angel2:

Aber wieso eigentlich immer "zurecht"?

Statt der TParams.GetDefault-Methode könnte man notfalls auch noch eine CreateParams-Funktion erstellen

Delphi-Quellcode:
type
  TParams = record
    class function Create: TParams; static;
    function Default: TParams;
    function paramA(i: Integer): TParams;
    function paramB(i: Integer): TParams;
    function ParamList: TParams;
  private
    paramAValue: Integer;
    paramBValue: Integer;
  end;

function CreateParams: TParams;





function CreateParams: TParams;
begin
  Result.Default;
end;

class function TParams.Create: TParams;
begin
  Result.Default;
end;

function TParams.Default: TParams;
begin
  Finalize(Self);
  FillChar(@Self, SizeOf(Self), 0);
  Result := Self;
end;

function TParams.paramA(i: Integer): TParams;
begin
  paramAValue := i;
  Result := Self;
end;

function TParams.paramB(i: Integer): TParams;
begin
  paramBValue := i;
  Result := Self;
end;

function TParams.ParamList: TParams;
begin
  Result := Self;
end;

Khabarakh 2. Apr 2010 17:53

Re: Über den Umgang mit vielen optionalen Parametern
 
Zitat:

Zitat von himitsu
Zitat:

Zitat von Khabarakh
Parameterlose Konstruktoren gibt es bei Records (zurecht) nicht.

OK, aber die parameterlose Static-Function sollte dennoch gehn. :angel2:

Jupp, deswegen habe ich sie ja vorgeschlagen :mrgreen: .
Zitat:

Zitat von himitsu
Aber wieso eigentlich immer "zurecht"?

Dann eben "zu Recht", lass mir doch meine kleine Rechtschreibschwäche :cry: .
Von Klassen ist ein Entwickler gewohnt: Wenn er ein Objekt in der Hand hat, ist davon auch der Konstruktor aufgerufen worden. Aber was sollte passieren, wenn du ein Record-Array erstellst? Die Record-Objekte sind ja schon "da", Klassen-Instanzen wären einfach nil; also müsste der Compiler durch das Array gehen und für alle Elemente den Default-Konstruktor aufrufen. Das hat der Programmierer aber wahrscheinlich doch wieder nicht erwartet, wir sind hier doch nicht bei C++ ;) .

Zitat:

Zitat von himitsu
Statt der TParams.GetDefault-Methode könnte man notfalls auch noch eine CreateParams-Funktion erstellen

Hee, wir haben uns bis jetzt doch so schön an OOP gehalten :zwinker: .

himitsu 2. Apr 2010 19:40

Re: Über den Umgang mit vielen optionalen Parametern
 
Dann wird es Zeit, daß hier endlich mal mir einem schönen POP oder ROP begonnen wird. :lol:


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:57 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