Artikelformat

Fluent Interfaces in PHP

Seit PHP 5 gibt es die Möglichkeit ein von einer Methode zurückgegebenes Objekt direkt zu referenzieren. Was das bringt zeig ich in diesem kleinen How To. Ich benutze das ziemlich oft um den Code etwas schlanker zu halten. Zuerst die übliche Vorgehensweise. Das folgende Beispiel speichert beliebige Werte in ein eindimensionales Array und mittels der magischen Methode __toString geben wir das Ganze dann aus. Ich habe dieses Beispiel gewählt weil ich das tatsächlich in einigen Projekte einsetze und weil es sehr einfach zu verstehen ist. Der Codeschnipsel stammt aus einem Templateparser, der die Aufgabe hat, bestimmte Felder durch bestimmte Werte zu ersetzen. Doch kommen wir einfach gleich mal zum Beispiel.

class Chain {

privat $_params;

	public function setParam($field, $value)
	{
	$this->_params[$field] = $value;
	}

	public function __toString()
	{
	print_r($this->_params);
	}
}

$chain = new Chain();
$chain->set('field1' , 'value1');
$chain->set('field2', 'value2');
$chain->set('field3', 'value3');

Was hier sofort auffällt, ist dass wir die Methode ‘set’ immer wieder mit Hilfe des Objekts $chain aufrufen. Das führt zwangsläufig dazu, dass bei mehreren Felder der gleiche Code unnötig in die Länge gezogen wird. Zum Glück lässt sich seit PHP5 ein von einer Methode zurückgegebenes Objekt direkt referenzieren. Das Einzige was nun noch geändert werden muss ist der Rückgabewert in der Methode ‘set’.

class Chain {

privat $_params;

	public function setParam($field, $value)
	{
		$this->;_params[$field] = $value;
		return $this;
	}

	public function __toString()
	{
		print_r($this->;_params);
	}
}

$chain = new Chain();

$chain->set('feld1', 'wert1')->set('feld2', 'wert2')

Wie wir nun sehen, hat sich der Code nicht wesentlich verändert, das Skript funktioniert nach wie Vor. Der Unterschied besteht nun darin, dass wir immer ein Objekt direkt referenzieren und damit beliebig viele Methoden nacheinander ausführen können. Dies nennt man Fluent Interface.

Autor: donbosco

Test Test

4 Kommentare

  1. Hallo,

    ich habe mich im Zuge meiner Bachelorarbeit sehr intensiv mit solchen Fluent Interfaces auseinander gesetzt. In deinem Beitrag verwendest du einen eher einfachen Ansatz ein Fluent Interface zu realisieren. Was ist wenn du z.B. eine bestimmte Reihenfolge der Methoden erzwingen möchtest? In meiner Bachelorarbeit habe ich dazu ein Werkzeug erstellt mit dem es nun möglich ist Diagramme zu erstellen aus denen direkt der nötige Programmcode generiert werden kann. In den Diagrammen beschreibt man nur noch welche Methode nach welcher aufgerufen werden darf. Der Rest wird automatisch generiert. Unter http://www.fluent-interfaces.com findet man einiges an Material zu meiner Lösung. Zwar wird zur Zeit nur JAVA unterstützt aber eine Portierung für PHP ist durchaus denkbar.

  2. Hallo Philip,

    danke für deinen Kommentar, du hast mit der Aussage natürlich absolut Recht. Es gibt durchaus oft Situationen, in denen eine bestimmte Reihenfolge erforderlich ist. So wie ich es gehandhabt habe, ist es eher ein simples Method Chaining. Es ist aber auch immer abzuwägen ob das wirklich nötig ist. Kann ich zum Beispiel durch meinen Code bereits eine richtige Verkettung erzwingen oder muss die implementierte Fluent Interface Klasse tatsächlich die Reihenfolge der Verkettung vorgeben.

    Mein Beispiel oben wird zum Beispiel für REST Interfaces gebraucht. Bei Rest Abfragen spielt es nicht eine unbedingt wichtige Roll,e welcher Parameter zuerst kommt. Fluent Interfaces wie du sie beschreibst würde ich bei SQL Statements zum Beispiel benutzen bzw. tue es dort auch. Hier spielt natürlich die Reihenfolge eine wichtige Rolle und ich stimme dir da auch absolut zu.

    Beispielhaft für alle Mitleser (Pseudocode)

    $db = new db(); // datenbankadapter erstellen
    $query = new Query()->select()->from(‘tabelle’)->params(array(‘spalte1′, ‘spalte2′)->where(‘spalte2′ > 2)->limit(5);

  3. Hallo donbosco,

    da geb ich dir natürlich vollkommen recht. Wie bei jedem Pattern muss man immer abwägen wann es richtig dies einzusetzen.

    Nur noch eine Anmerkung zum “einfachen” Ansatz. Ich habe viele Beispiele gesehen in denen die Realisierung des Fluent Interfaces direkt in der Typen vorgenommen wird die durch die Verkettung von Methoden verändert werden sollen. Zwar mag in diesen Beispielen die Reihenfolge keine Rolle spielen aber es besteht die Gefahr, dass wir unsere Entitäten (z.B. User o.ä.), Value Objects etc. durch Methoden “verschmutzen” die eigentlich garnichts mit diesen zu tun haben. Daher bin ich fast immer der Meinung, dass man zumindest das Fluent Interface selbst von dem betroffenem Objekt trennen sollte und bei der Schnittstelle nur auf bestehende Funktionalitäten zurückgreifen sollte.

    Das entspricht vom Prinzip der Trennung zwischen Services, Entitäten und Value Objects wie man sie aus dem Domain-Driven Design kennt.

    Das soll hier natürlich nicht zu einer Regel herhoben werden sondern nur zur Vorsicht mahnen :)

  4. Hallo Phillip,

    danke für deinen ausführlichen Kommentar. Ich habe die Gelegenheit genutzt, mir mehr Gedanken darüber zu machen und den Artikel etwas zu überarbeiten.

Hinterlasse eine Antwort

Pflichtfelder sind mit * markiert.

*