Viele Methoden oder viele Parameter?

suntrop

Erfahrenes Mitglied
Hi

Ich brauche auf einer Website unterschiedliche Ausgaben von Artikelpreisen. Mal netto, mal brutto, manchmal der Stückpreis, manchmal …
Ich hatte bisher eine Klasse mit (zu) vielen Methoden, aber das Ganze gefällt mir nicht … es herrscht da eher Chaos und Doppelung :-(

Jetzt wollte ich das erneuern und winde mich um eine gute Entscheidung, und wäre hier für Ratschläge sehr dankbar!
PHP:
$product_xyz->price(); // 58.8 - Preis für die kleinste Paketmenge (12 Stück)
$product_xyz->price(48, true); // 279.89 - Preis für 48 Stück, brutto

$product_xyz->pricetag(); // € 58,80 - Wie price(), aber als formatierter String
$product_xyz->pricetag(48, true); // € 279,89
$product_xyz->pricetag(NULL, true); // € 58,80

$product_xyz->unitprice(); // 4.9 - Der Einzelpreis, in der "kleinsten" Paketmenge
$product_xyz->unitprice(1, true); // 5.83 - Einzelpreis inkl. MwSt
$product_xyz->unitprice(9999); // 4.2 - Einzelpreis bei "größter" Paketmenge (realistisch Paketmengen im unteren dreistelligen Bereich)
$product_xyz->unitprice(9999, true); // 5 - Einzelpreis bei "größter" Paketmenge, inkl. MwSt

$product_xyz->minimum; // Kleinste Paketmenge (idR 12 Stück)
$product_xyz->maximum; // Größte Paketmenge (idR 10 x kleinste Üaketmenge)
$product_xyz->graduation; // Abstufung der Paketmengen (idR in 12er Schritten)
$product_xyz->isGraduation(13); // true|false ob die Menge in der Abstufung liegt

Jetzt habe ich aber auch überlegt, das ganze als Fluent Interface zu machen.
PHP:
$price_gross = $product_xyz->price()->amount(25)->gross()->pricetag();

Meine grundsätzliche Frage ist, sollte ich eher sehr viele Methoden erstellen die nur ganz wenig machen? Also
price()
priceNet()
priceGross()
pricetagNet()
pricetagGross()
untipriceNet()
unitpriceGross()
etc.
Oder sollte ich eher Parameter der Methoden nutzen?
price(48, true, 'formatted', 'custom')

Kann man hier grundsätzlich eher zum einen oder anderen Raten?
 
Hey suntrop,
das ist ein typischer Fall in den man nicht den einen richtigen Weg sagen kann.
Du wirst hier bestimmt von einigen zu hören bekommen mache eine Methode die mehreres kann, damit die Funktion übersichtlich bleibt und von anderen, dass du lieber mehrere Methoden mit einen einfachen Aufruf.

Nutze das verfahren, was dir am meisten zusagt. Das wichtigste ist eher wenn du dich entschieden hast, dann mache es in allen Klassen so. Es ist nichts schlimmer, als immer zu überlegen, wie hat man es in einer Klasse nun gelöst.

Ich weiß das war jetzt nicht unbedingt in allen Bereichen hilfreich. Ich hoffe dennoch das Hilft dir ein wenig.
 
$product_xyz->price(48, true); // 279.89 - Preis für 48 Stück, brutto
Boolesche Parameter finde ich in vielen Fällen unschön, weil man nicht weiß, was sich dahinter verbirgt. In Sprachen ohne "named parameters" (z. B. Python hätte die) würde ich Enums nutzen.

$product_xyz->price(); // 58.8 - Preis für die kleinste Paketmenge (12 Stück)
Dass das den Preis für die kleinste Paketmenge nimmt, ist nicht offensichtlich. Ich würde die Methode umbenennen zu "priceForSmallestQuantity()" oder "priceSmallest()", wenn du es kürzer magst.

price(48, true, 'formatted', 'custom')
Lieber keine magic strings, sondern Konstanten oder Enums. Und auch lieber als ein zusammengesetztes Argument, sonst muss man darüber nachdenken, warum zuerst 'formatted' und dann 'custom' kommt.

An sich würde ich in Preisberechnung und -darstellung trennen! Meiner Meinung nach gehören die nicht einmal in dieselbe Klasse.
Ich würde da eher an so etwas denken:
PHP:
formatPrice($product_xyz->price(48));

Wenn du solchen Code aber oft schreiben musst, könnte man darüber nachdenken, Preisberechnung und -formatierung in einer Hilfsfunktion/-klasse zu kapseln. Der Bequemlichkeit halber könnte man hier einen Schritt zurück von der ultrasauberen Kapselung treten. (Wohlgemerkt wäre jede Codebasis unnutzbar, wenn man alles sauber kapseln würde. Man hätte unglaublich viele Ebenen von Abstraktionen und Indirektionen.)
 
Danke für eure Antworten. Das hilft mir weiter.

Lieber keine magic strings, sondern Konstanten oder Enums. Und auch lieber als ein zusammengesetztes Argument, sonst muss man darüber nachdenken, warum zuerst 'formatted' und dann 'custom' kommt.
Das habe ich nicht verstanden. Hast du ein Link zu einem Beispiel? Wie sieht sowas aus?

Wenn du solchen Code aber oft schreiben musst, könnte man darüber nachdenken, Preisberechnung und -formatierung in einer Hilfsfunktion/-klasse zu kapseln.
Ja, ich habe das recht oft und ich finde die Verschandelung von Funktionen eher hinderlich (mehr schreiben, unleserlich).

Ich habe so viele Methoden die fast das gleiche machen, nur mit kleinen Unterschieden. Deshalb wollte ich das wie eine Kaskade aufbauen.
Denn die Preisauszeichnung (7,98 EUR) baut auf dem Bruttopreis auf, der auf dem Nettopreis, der wiederum auf dem Stückpreis und der Menge, der Stückpreis wiederum wird auch durch die Menge (Mengenrabatt) definiert, und dann kann es noch sein, dass der Stückpreis durch eine "Sonderformel angepasst" (VIP Kunden) wird. Keine Ahnung, ob das verständlich ausgerückt ist :)

---
EDIT

Wäre es eine Lösung, wenn ich bestimmte Dinge, statt als Parameter zu übergeben, als Property setze? Also z.B. $product_xyz->quantity = 48; Und in der Methode dann später über $this->quantity die Anzahl abrufe.
 
Zuletzt bearbeitet:
Das habe ich nicht verstanden. Hast du ein Link zu einem Beispiel? Wie sieht sowas aus?
Zu "magic strings" siehe analog "magic numbers": https://en.wikipedia.org/wiki/Magic_number_(programming)#Unnamed_numerical_constants.

Beispiel:
PHP:
$product_xyz->price(48, GROSS | STRING_FORMATTED)

Aber ich finde es unschön, dass eine Funktion je nach Argument einen unterschiedlichen Return Type hat. Viele Programmiersprachen unterstützen das auch gar nicht.
Einen Link habe ich nicht parat. Aber such mal nach "boolean parameters bad".

Wäre es eine Lösung, wenn ich bestimmte Dinge, statt als Parameter zu übergeben, als Property setze? Also z.B. $product_xyz->quantity = 48; Und in der Methode dann später über $this->quantity die Anzahl abrufe.
Wenn du bereits einen Preis ausgegeben hast und beim zweiten Preis vergisst, die Quantität zu setzen, so wird price() stillschweigend ein falsches Ergebnis produzieren. Daher finde ich, dass das keine akzeptable Lösung ist.

Was hälst du von Folgendem?
PHP:
$product_xyz->price(48, GROSS | STRING_FORMATTED)
$product_xyz->price(48, NET | VIP_CUSTOMERS | STRING_FORMATTED)
Wobei ich mit VIP_CUSTOMERS nicht ganz zufrieden bin. Was ist, wenn sich die Formel ändert? Führst du dann VIP_CUSTOMERS2, VIP_CUSTOMERS3 usw. ein? Dasselbe Problem betriff eigentlich auch GROSS vs NET, falls sich die MwSt ändert - was aber wesentlich seltener der Fall sein mag.
 
Aber ich finde es unschön, dass eine Funktion je nach Argument einen unterschiedlichen Return Type hat.
Wobei ich mit VIP_CUSTOMERS nicht ganz zufrieden bin. Was ist, wenn sich die Formel ändert? Führst du dann VIP_CUSTOMERS2, VIP_CUSTOMERS3 usw. ein?
Der heilige Gral ist das dann nicht? Und ja, ich habe im Hinterkopf schon eine zweite Formel.

Irgendwie blicke ich jetzt weniger durch als vorher :)
Wenn das eine wie das andere nicht optimal ist, was wäre dann mit der Lösung "ganz viele" Methoden zu erstellen? Also für jeden Kleinkram sozusagen eine eigene Methode. Dann endet das zwar mit rund 30 Methoden, aber es wäre flexibel auszubauen. Nur die Code-Doppelung innerhalb der Methoden müsste ich dann in Griff bekommen.
 
Code Doppelungen kannst du mit weiteren Methoden umgehen, die innerhalb von deinen Methoden aufgerufen werden.

Diese Methoden kannst du ja auch als private oder protected definieren, dadurch ist die Klasse nach außen zumindest etwas kleiner und du kannst Änderungen an den Berechnungen ohne großen Aufwand durchführen.

z.B. so
PHP:
...
private function Calculate( int $amount, int $unit_cost, int $tax ) {
   return $amount * $unit_cost * $tax;
}

public function GetUnitPrice( ) {
   return $this ->Calculate( 1, $this ->unit_cost, $this ->tax );
}

public function GetPrice( ) {
   return $this ->Calculate( $this ->amount, $this ->unit_cost, $this ->tax );
}
...
 
Zurück