Sven Rautenberg: Parameter zum Klassenaufruf zwischenspeichern

Beitrag lesen

Moin!

Also ich übergebe einer Funktion nacheinandere mehrere verschiedene Objekte mit verschiedenen Konstruktoren. Ausserdem erhält die Funktion 2 weitere Parameter die immer benötigt werden.

foobar(new Object1($int, $string1), $wichtig1, $wichtig2);
foobar(new Object2($string1, $string2, $string3, $object), $wichtig1, $wichtig2);


>   
> Jetzt ist es aber so, dass in der Funktion anhand der 2 letzten Parameter eine Unterscheidung getroffen wird ob das Objekt aus dem ersten Parameter überhaupt benötigt wird. Das Objekt ist zu dem Zeitpunkt aber schon instanziert und das gefällt mir nicht.  
  
Da würde ich jetzt erstmal ganz simpel sagen: Na und?  
  
Und zweitens würde ich mich fragen, was das denn für eine grausame Funktion ist?  
  
Die ist aus mehreren Gründen grausam:  
  
Erstens hat sie eine mangelhafte Signatur: Optionale Elemente gehören ans Ende der Parameterliste, und offenbar ist das Objekt optional.  
  
Zweitens: Wieso verlangt die Funktion ein Objekt, dass sie hinterher nicht braucht? Wenn man das offenbar anhand des übergebenen Parameters ermitteln kann, warum wird das nicht außerhalb der Funktion ermittelt?  
  

> Deshalb möchte ich der Funktion foobar($object, $wichtig1, $wichtig2) gern das Instanzieren der Objekte überlassen. Mein Problem ist jetzt, dass ich mir nicht sicher bin, wie ich die Parameter zu den Konstruktoren am elegantesten übergebe.  
  
Gar nicht!  
  
Wenn die Funktion die Objekte herstellt, erfüllt sie eine weitere Aufgabe zusätzlich, die ihr nicht zusteht. Außerdem hast du dir damit ziemlich komplexen Code eingehandelt, der in der Funktion zum Erstellen der Objekte enthalten sein muss, denn ich vermute mal, dass es nicht nur genau ZWEI verschiedene Objekte sind, die du da erstellen willst, sondern potentiell noch ein paar mehr. Und alle haben sie unterschiedlich lange Parameterlisten des Konstruktors (was auch wieder sehr fragwürdig ist im Hinblick auf Vererbung und Selbstähnlichkeit, aber sofern die Objekte zumindest alle dasselbe Interface gegenüber der Funktion implementieren, kann das mit den notwendigen Unterschieden der Unterklassen durchaus begründet sein).  
  
Du willst in der Funktion keinen Code haben, der dir Objekte herstellt.  
  
Was man sich bauen könnte: Du übergibst der Funktion anstelle des Objekts ein Callable (in diesem Fall vermutlich eine anonyme Funktion), die du in der Funktion aufrufen kannst, wenn du das Objekt brauchst. Dann weiß das Callable, wie das Objekt zu instanziieren ist, und gibt es zurück.  
  
~~~php
  
foobar(function () use ($int, $string1) { return new Object1($int, $string1);}, $wichtig1, $wichtig2);  
foobar(function () use ($string1, $string2, $string3, $objekt) { return new Object2($string1, $string2, $string3, $objekt);}, $wichtig1, $wichtig2);  

In der Funktion dann:

  
if ($wichtig1 == "braucht objekt") {  
    $obj = $callable();  
}  

Diese Lösung ist aber aus meiner Sicht auch sehr eklig.

Zum einen: Deine Funktionssignatur müsste sich entweder generell nicht um den übergebenen Parameter kümmern (hinsichtlich Typehinting - ich habe das blöde Gefühl, dass sie es jetzt schon nicht tut), und innerhalb der Funktion wäre dann zu prüfen, ob der übergebene erste Parameter ein Callable oder ein anderweitiges Objekt ist. Nur bei einem Callable muss man das Objekt erst durch einen Aufruf auspacken.

Die Alternative mit Typehint würde erzwingen, dass die Funktion IMMER ein Callable erwartet anstelle eines Objektes, und das würde alle normalen Funktionsaufrufe mit existentem Objekt sehr unschön verkomplizieren.

Ich würde also im Ganzen davon abraten, das so umzusetzen.

Ich hab aber im Moment auch noch keine Idee, was man da optimieren könnte. Deine Beschreibung deutet an, dass die Klassenstruktur sowieso nicht optimal ist. Du hast außerdem außer deinem Gefühl für Unschönheit noch kein Argument GEGEN die Instanziierung gebracht. Das einzige Argument, das mir einfiele, wäre Performance - und das dann gültige Gegenargument ist: Tu nix im Konstruktor, außer die Parameter im Objekt lokal abzuspeichern.

Das einzige dann noch verbleibende Gegenargument wäre Speicherverbrauch. Um den kommst du aber bei keiner Lösung drum herum, denn ein Array mit den Konstruktorparametern kostet auch Speicher, ebenso wie das Callable.

Ich habe über 2 Varianten nachgedacht.

Variante 1: foobar("Klassenname", $objectparam1, $objectparam2 /* , $objectparam3, ... */, $wichtig1, $wichtig2);

Optionale Parameter gehören ans Ende der Parameterliste. Wenn du sie nach vorne setzt, wird dein Funktionscode sofort hundertmal unübersichtlicher, weil du jetzt eine Standardfunktion von PHP, nämlich das Parsen der Parameter, selbst erledigen musst. Und sowas geht auch nicht umsonst, sondern kostet ebenfalls CPU-Zeit. Vermutlich sogar mehr, als das sinnlos instanziierte Objekt zu erstellen.

Variante 2: foobar("Klassenname", array("publicvar1"=>$string1, "publicvar2"=>$string2), $wichtig1, $wichtig2);

Das ist genauso schlecht. Deine Funktion hat nicht die Aufgabe, Objekte zu bauen.

- Sven Rautenberg