Nov
24
2010

Funktionsargumente – von niladic bis polyadic und zwischendrin

Öh ja der Winter ist nun offiziell hier in der fränkischen Schweiz angekommen, das ist !schön.
Aber eigentlich wollte ich mal eine kleine Diskussionsrunde starten. Sicherlich kennen viele von euch das Buch “Clean Code” von Robert C. Martin und dem zu Folge auch seine Ausführungen über Funktionsargumente.
Für alle anderen folgt hier noch ein kurzer Abriss über den Inhalt:

The ideal number of arguments for a function is zero (niladic).

Prinzipiell wird zwischen 5 Arten unterschieden:
1. Funktionen mit keinem Argument (niladic) siehe quote von oben.
2. Funktionen mit einem Argument (monadic) was als 2t Beste Möglichkeit angesehen wird
3. Funktionen mit zwei Argumenten (dyadic) sehr nahe an monadic dran
4. Funktionen mit drei Argumenten (triadic) diese Variante sollte wenn möglich vermieden werden
5. Funktionen mit mehr als drei Argumenten (polyadic) selbst wenn mehr als 3 Argumente im speziellen Anwendungsfall berechtigt oder angebracht sind, sollte es dennoch gänzlich vermieden werden.

Als Gründe gegen mehr Funktionsargumente, führt Martin, zum einen an, dass diese auf einer anderen Abstraktionsebene liegen können, als der eigentliche Funktionsname und man somit gezwungen wird, mehr über die eigentliche Funktion zu wissen als man zu dem Zeitpunkt des Aufrufs wissen muss.
Als Beispiel rufen wir uns die Funktion “shorten()” unserer Bitly Klasse ins Gedächtnis ;) diese erwartet nur ein Argument als Übergabeparameter und zwar die Url die es zu kürzen gilt. Doof wäre es jetzt wenn diese zusätzlich die Instanz eines Zend_Http_Clients erwarten würde um überhaupt die URL kürzen zu können, jaja ich weiß doofes Beispiel aber Übertreibung veranschaulicht.
Zum anderen führt er die Schwierigkeiten beim testen einer Funktion mit vielen Funktionsargumenten an, da man hier zusätzlich eine Reihe von Testfällen schreiben müsste, um sicherzustellen, dass die unterschiedlichen Kombinationen der Parameter auch richtig funktionieren.
Des weiteren fördert eine lange Übergabeliste nicht unbedingt die Lesbarkeit des Codes.

Da wir jetzt wissen, dass Punkt 4. und 5. eher auf der dunklen Seite der Macht anzusiedeln sind und lt. Martin als bad practises angesehen werden, möchte ich gerne wissen wie ihr es so mit euren Funktionsargumenten haltet und wie eure Meinung bzgl. der Ausführungen von Martin sind.

Ist die ganze Geschichte eine Glaubensfrage? eher Kontextabhängig? dogmatisches Gesetz? seid ihr eher Fans von argument objects? option arrays?

Über den Autor: Patrick Einwag

12 Kommentare Kommentar schreiben

  • Sinnvoll wäre es schon eine Zend_Http_Client Instanz zu übergeben. (also wenn du es wirklich bräuchtest ^^)

    Ich sag nur Dependency Injection und Testbarkeit.

    Grunsätzlich sollte es überschaubar sein … aber spätestens wenn man ein eigenes MVC schreibt, benötigt man 4 Params. Dafür ist es voll Testbar … hm was ist wohl besser ;o)

    Die Frage wäre auch wie ein Entwickler das in der Praxis umsetzt. Wahrscheinlich übergibt er dann statt 5 Paramater ein Array mit den entsprechenden Elementen. Hmmmmm das wäre auch Ziel verfehlt :o )

  • Das Thema DI wurde in Zend_Service_Abstract eigentlich schon berücksichtigt, von dem die Klasse ja auch erbt. In Form von Settern und Gettern für den Client. Falls kein Client vorher gesetzt wurde wird eben ein Standard Http Client zurückgegeben.

  • Naja gut wie gesagt kein gutes Beispiel. ;)

  • Jein, es ist halt prinzipiell die Frage, ob man einen Http_Client als Übergabeparameter bei einer shorten() Funktion erwarten würde. Wenn diese setClientAndShorten() heißen würde schon, nur wäre so eine Funktion weniger sinnvoll.

  • Huhu, also wenn man sich darauf beruft übersichtlichen Code zu schreiben, ist es sehr sinnvoll die Übergabe von Variablen so gering wie möglich zu halten. Ich persönlich bin leider eine faule Person und schreibe auch häufig mehrere Variablen in eine Funktion. Geschweige denn extra irgendwelche geter und seter anlegen hasse ich wie die Pest. Aber manchmal sollte man sich Gedanken machen wie schreibe ich einen gut lesbaren und verständlichen Code.

    Der Gedankenansatz und die Äußerung finde ich gerechtfertigt. Gegebenenfalls sollte ich mal an meiner Faulheit ein wenig arbeiten. Aber wenn sowieso niemand in mein Code schaut, dann ist es mir auch egal wie ich den Spaß schreibe. Aber wie sagt man jeder Programmierer ist für jede Zeile zu faul. xD

  • @Nico

    Setter und Getter sind sehr gut geeignet um Funktionen mit möglichst wenig Parametern zu versehen. Ausserdem gib es im Zend Studio und ich glaube in andern IDEs auch die Funktion Setter und Getter automatisch zu setzen. Dann nur noch den entsprechenden Code einfüllen wenn nötig.. dann kann man wenns Not tut auch gleich ein Fehlerbehandlung implementiern oder eine Exeption werfen.

    Wenn das ganze dann auch noch (wenn möglich und sinnvoll) als “Fluent Interfaces” implementiert, und die öffentlichen Methoden intutiv benannt sind dann kann man den Code schön lesen….

  • Guten Morgen,

    mal angenommen wir verzichten auf Parameter, und regeln das alles für set/get.

    Wie reagieren eure Methoden wenn gewissen Voraussetzungen nicht gegeben sind. Beispielsweise der Client nicht gesetzt ist?

    Exceptions mit eindeutigen Meldungen würden mir einfallen?

  • @Arne

    kommt auf die schwere des Fehlers an, Exceptions wenn nötig für die Anwendung ist, Fehlerbehandlung wenn möglich. allerdings bin ich kein Freund der magischen Methoden __get / __set ausprogrammierte setter / getter sind da besser, finde ich ;-)

  • Also ich habe das Buch auch gelesen und bin absolut der gleichen Meinung, versuche daher möglichst die Argumente selbst als Objekt zu realisieren.
    Der Array Ansatz gefällt mir nicht wirklich, da die Verwendung des Arrays nicht wirklich anders ist, als wenn ich meine 5 Argumente direkt in die Signatur haue, außer dass ich dann auch nicht mehr erkennen kann, was überhaupt erwartet wurde.
    Kann das Buch übrigens empfehlen, auch wenn einige Ausführungen doch etwas stark mit Quellcode überzogen sind…

  • Ich denke Martin setzt wohl eher da an, die Methoden möglichst klein aufzuteilen, was zwar bedeutet, der Client muss dann evtl. 3 oder 4 Methoden mit wenigen Parametern aufrufen, anstatt bereits einer Methode die mehr als 3 Parameter nimmt.
    Das erhöht die Lesbarkeit beim Code des Clients enorm, da ich weiß wie der Ablauf zu meinem Ergebnis stattfindet aber immer noch nicht wie.
    Auch ist ein wichtiger Grund die Parameteranzahl gering zu halten, dass es wohl kaum eine Methode geben wird die NUR auf einer Abstraktionsebene fünf Parameter abhandelt.

  • @Marco

    Korrekt! ich bevorzuge Methode die möglichst wenigst Code enthalten und nur eine Aufgabe erfüllen ;-) , keine großartigen ausschweifenden
    If.. elseif…else bzw switch… case Konstrukte (wobei sich das leider in Controllern nicht immer so realisiern lässt wie gewünscht)

  • Hmm, ich halte es da eher mit dem o.g. Hinweis zu DI (ohne Container). Wenn nur eine Methode ein bestimmtes Objekt benötigt, will ich eigentlich nicht mein gesamtes Objekt mit dieser zusätzlichen Property zumüllen. Erst recht nicht, wenn die Methode vielleicht nie aufgerufen wird.
    Zumal es auch nicht ausgeschlossen ist, dass das Objekt zu einem Zeitpunkt erstellt wird, zudem das Parameterobjekt noch gar nicht existiert. Und die Methode erst in einem späteren Kontext verwendet wird. IMHO alles wichtige Punkte. Der Beitrag mit der Frage nach zusätzlicher Prüfung auf Vorhandensein des Hilfsobjekts ist auch valid. Im Endeffekt läuft es dann nämlich darauf hinaus, das Parameterobjekt bereits im Konstruktor zu übergeben, um sicher zu sein. Oder man muss in jeder einzelnen Methode immer alles abprüfen. Inklusive Exception-Catch im aufrufenden Kontext usw. Performance ist dann auch noch ein Gedanke.

    Noch schöner ist natürlich nur eng mit dem Objekt verwandte Interfaces an die Methodensignatur koppeln zu müssen. Noch besser, wenn man bereits außen die nötigen Daten holt und die Methode möglichst atomar gestalten kann.

Kommentar schreiben