PHP Blogger

Startseite Schreib mir ne Mail! RSS Abo Webnews

Code-Optimierung: Rückgabewerte, Fabriken, 0, NULL, false und 1

Oh wie kryptisch. Heute nur ein kurzer Beitrag rund ums Thema Rückgabewerte und Code-Optimierung was Lesbarkeit betrifft. Bin letzte Woche eher zufällig mit einem Kollegen über das Thema gestolpert und obwohl es so trivial ist, hat es mich nicht losgelassen. Und zwar geht es um die Wert-Range von false. Was wird eigentlich alles als false im weitesten Sinne interpretiert?

PHP nimmt es bei Wertvergleichen standardmäßig nicht so genau. Die Vergleichs-Operatoren gleich bzw. ungleich (== und !=, oh jetzt kratze ich wirklich an den Basics) sind zunächst ja nicht Typen beeindruckt. Weniger was Lederjacken-Träger angeht, als die Werte von Variablen.

Für ganz Genaue müssen da schon die Operatoren === (Identisch) und !== (Nicht identisch) herhalten. Mit welchen Operatoren man Variablen vergleichen sollte, ist wieder mal Geschmackssache. Ich halte das in der Regel wie folgt: Geht es um einen wichtigen Vergleich - eine Typ-Validierung in einer Setter-Methode beispielsweise - nutze ich die strengen Operatoren. Bei einer unkritischen Abfrage von Rückgabewerten (wie gesagt, sie muss unkritisch sein) bediene ich mich oft bei den Standard-Operatoren.

Und genau bei Rückgabewerten will ich mich einhaken. Es geht um das Thema Lesbarkeit. Wir holen uns mal schnell ein Objekt aus einer Fabrik:

$objekt= $fabrik->neueInstanz();

Nehmen wir mal an, unserer Fabrik gelingt es nicht ein neues Objekt produzieren zu können. Wer übernimmt die Fehlerbehandlung und wirft z.B. eine Exception? Die Fabrik selbst? Denkbar. Meiner Meinung nach (ich denke, das ist fallabhängig zu betrachten) sollten Fabriken recht dumm und simpel sein und sich auf das konzentrieren, was sie können: Neue ObjektInstanzen. Wenn überhaupt sollte ein Exception geschmissen werden.

Was wenn nicht? Was, wenn man auf einen Rückgabewert wie null testen möchte? Man könnte schreiben:

if($objekt == null) { ... }

oder

if($objekt == false) { ... }

Letzteres funktioniert, weil null im Normalfall als false interpretiert wird. Hm. Was noch? Als false wird sonst noch 0 (numerische Null) gewertet. Zahlen größer oder kleiner Null werden als true gewertet, ebenfalls Variablen, die mit gültigen Objekten besetzt sind.

Einer meiner Lieblings-Operatoren ist übrigens NOT. Man könnte also auch schreiben:

if(!$objekt) { ... }

Ach, wie herrlich kurz und präzise das ist. Und zugleich super lesbar - Ich sprech es mal laut nach:

“If not objekt then …”

Dagegen klingen die ausgesprochenen ersten beiden Versuche richtig sperrig: Also ich liebe das. Kurz und knackig - aber wer die Lesbarkeit noch weiter verbessern möchte, kann auch auf eine Wrapper-Variable zurückgreifen:

$objekt_nicht_verfuegbar= (!$objekt);
if($objekt_nicht_verfuegbar) { ... }

Auch sehr gut. Der Vollständigkeit will ich noch erwähnen, das man das natürlich auch mit einer Exception abfangen kann. Was besser ist, muss jeder Entwickler selbst entscheiden, wenn er vor dem Problem sitzt - denn Exceptions müssen nicht zwangsweise sicherer sein als Bedingungs-Abfragen.

Ähnliche Artikel:

  1. Lesbaren Code schreiben
  2. Die Mutter aller Objekte
  3. Hook- und Callback-Funktionen (Teil 3)

Sascha Presnac meint dazu:

15. Dezember 2009 um 11:22

Sehr schöner Artikel, glänzt vor pragmatismus, ich mag das.
Ein Kommentar zur Wrapper-Variable: In deinem Fall wird bei $objekt_nicht_verfügbar das $objekt nur negiert, d.h. wenn deine $objekt false ist klappt das ganze gut mit der Lesbarkeit. Allerdings finde ich, es ist besser lesbar, einfach ein !$objekt zu schreiben, damit drückst du ja alles aus und es ist zudem viel kürzer.

timi meint dazu:

15. Dezember 2009 um 17:46

@Sascha: Ja das ist tatsächlich Geschmackssache - ich gebe Dir Recht: Auch mir gefällt wie im Artikel beschrieben, die kurze Variante besser…

ridcully meint dazu:

21. Dezember 2009 um 16:45

Ich bevorzuge hier lieber (null === $objekt), denn hier ist eindeutig lesbar, dass ich im fehler erwarte, dass null zurück kommt. der verwendete code impliziert also bereits den typ, den ich im fehler erwarte.

außerdem wird dadurch, dass das objekt ans ende gestellt wird, gleich vorgebeugt, dass eine zuweisung durch typo entsteht. null = $objekt gibt eine fehlermeldung im interpreter, wohingegen $objekt = null eine korrekt zuweisung wäre.

Maggus meint dazu:

21. Dezember 2009 um 20:32

Moin,

ich bevorzuge in diesem Falle
is_null($objekt) //null
is_numeric($objekt) //0
und dergleichen.

Spricht mich irgendwie eher an und wirkt imho nicht so PHP-typisch unsauber

timi meint dazu:

22. Dezember 2009 um 09:57

@ridcully: Wie bereits angesprochen kann man diese Dinge natürlich bestens nach eigenem Gusto handhaben - trotzdem muss ich gestehen, das ich an Deiner Version wenig Gefallen finde… Ich würde in Deinem Fall zwischen lesbar und erkennbar unterscheiden. Während ich den Vergleich nicht so dolle lesbar (= sprechend) finde, ist er natürlich trotzdem eindeutig zu erkennen. Das wäre er aber auch, wenn Du den Vergleich umkehrst.

Der Vorteil, das Du noch mal logisch zwischen Vergleich und Zuweisung abgrenzt, ist meiner Meinung nach bei einem typisierten Vergleich nicht notwendig. Man müsste ja gleich zwei = Zeichen vergessen. Das passiert wahrscheinlich nur sehr selten und ist in der Regel nur ein Flüchtigkeitsfehler der sehr schnell bemerkt wird.

Vor dem Fall, das Du nur ein = Zeichen vergessen wirst, schützt Du Dich nicht. Und dieser Fall ist wesentlich wahrscheinlicher. Auch die Auswirkung wird nicht so schnell aufzuspüren sein, wie bei einer Zuweisung - denn es bleibt ja bei einem Vergleichsoperator und der Vergleich wird sehr wahrscheinlich wie erhofft funktionieren. Wie gesagt: Geschmackssache- meinen trifft es aber definitiv nicht.

@Maggus: Für Vergleiche auf bestehende, sprechende Funktionen zurückzugreifen ist natürlich eine feine Sache und durchaus legitim. Damit könnte ich mich auch anfreunden, wenn Du nicht das PHP-typisch-unsauberste machen würdest, was PHP Dir (leider) anbietet: Globale Funktionen.

Das trübt das Vorgehen ungemein und daher behaupte ich mal ganz frech, ist ein sauber in einer sprechenden Variable verpackter Vergleich technisch besser gelöst. Aber auch hier gilt: Alles hat seine Daseins-Berechtigung - für mich allerdgins mit dunklen Flecken. Trotzdem Danke für den interessanten Hinweis!

Maggus meint dazu:

22. Dezember 2009 um 12:31

Moin,

also das finde ich jetzt wiederum nicht so schlimm. is_null ist ja eine integrierte Core-Funktion. Es gibt halt im PHP-Core Funktionen, die ganz hilfreich sind gerade wenn es darum geht, wenn man mal die Sicherheit braucht, die die Typenlosigkeit leider nicht bietet. Und ob ich da jetzt per PHP::is_null oder is_null zugreife, ist mir relativ Banane.

Ein anderes Beispiel wäre array_key exists auf $_GET angewendet. Das ist ja anders gar nicht zu lösen, wenn nur eine Variable ohne Wert quasi als Flag (?test=1&flag) übergeben wird.
=>Also den Rückgriff auf bestehende Funktionen der Sprache, bloß weil nicht gekapselt in eine Klasse, finde ich nicht unsauber. PHP ist halt nicht Java.

Grüße ;-)

Manuel Grundner meint dazu:

12. Januar 2010 um 18:03

Ich finde das gerade bei Factories dieser Vergleich nicht sinnvoll ist, da eine Factory nie null zurück geben sollte.
Wie du schon angemerkt hast, sollte die Fabrik die Exception werfen, sie ist ja auch für das Erzeugen des Objekts verantwortlich. Wenn sie das nicht schafft soll sie eine Exception werfen.
Und wenn eine Exception nicht sinnvoll erscheint, dann sollte ein NullObject zurückgegeben werden, das von der Basis-Klasse oder dem erwarteten Interface erbt/implementiert.
Wirkt sich auch sehr gut auf die Testbarkeit des Systems auf, und hilft den Code in der richtigen Domaine zu halten.

Lg Manuel
Ps.: Sollten trotzdem Typenüberprüfungen anstehen bevorzuge ich is_null() auch wenn es nicht ganz so flott wie $object === null ist, aber die Lesbarkeit meines Erachtens verbessert wird. Es liest sich mehr wie einen Satz:
if(is_null($object))

RSS für Kommentare zu diesem Artikel · TrackBack URI

Schreib Deine Meinung