Das Entwurfsmuster TemplateMethod kennen und nutzen sicherlich viele von euch. Spätestens, wenn irgendein Framework im Einsatz ist, hat man in der Regel damit zu tun, um z.B. an verschiedenen Stellen im Code eigene Funktionen in einen Ablauf einzuhängen.
Das Prinzip ist einfach: in der (abstrakten) Basisklasse wird eine leere Methode definiert, die durch eine konkrete Klasse überschrieben werden kann.
Der Vorteil ist recht offensichtlich. Die Methode kann ohne vorherige Prüfung verwendet werden und das spart an vielen Stellen unnötigen Code. Aus meinem ästhetischem Empfinden der richtige Weg.
Aber auch den anderen Weg habe ich auch schon desöfteren gesehen und mein Bauchgefühl hat ja auch nicht immer recht. Vielleicht ist der schöne Weg ja auch der langsamere?
Gefühl ist gut, Zahlen sind besser! Also habe ich schnell drei Testklassen geschrieben und probiere die verschiedenen Möglichkeiten durch.
Variante 1: Wir verlassen uns darauf, dass die Methode aufgerufen werden kann.
<?php class Test1 { public function machWas() { $this->machWasAnderes(); } public function machWasAnderes() { } }
Variante 2: Wir prüfen, ob die Methode existiert und finden nichts.
<?php class Test2 { public function machWas() { if (method_exists($this, 'machWasAnderes')) { $this->machWasAnderes(); } } }
Variante 3: Wir prüfen, ob die Methode existiert und rufen sie auf.
<?php class Test3 { public function machWas() { if (method_exists($this, 'machWasAnderes')) { $this->machWasAnderes(); } } public function machWasAnderes() { } }
Zum Vergleich habe ich das folgende Script verwendet.
<?php foreach (array('Test1', 'Test2', 'Test3') as $class) { $object = new $class(); $start = microtime(true); for($i=0; $i<100000; $i++) { $object->machWas(); } echo $class . ' = ' . round(microtime(true) - $start, 5) . '<br/>'; }
Ausgabe:
Test1 = 0.44767 Test2 = 0.48588 Test3 = 0.70881
In mehreren Durchläufen waren “Test1″ und “Test2″ immer fast gleich auf, nur “Test3″ brauchte etwas länger, weil in der Klasse die Prüfung und die Ausführung der Methode stattfindet.
Fazit: Der Einsatz von Schablonenmethoden lohnt sich doppelt, da der Code übersichtlich bleibt und auch die Geschwindigkeit etwas besser ist.
11.01.2012 um 09:30 Uhr
Wenn es nur darum geht sicherzustellen, dass eine Methode in einer Klasse vorhanden ist kann man auch einfach ein entsprechendes Interface definieren und die Klasse implementiert dieses dann.
method_exists
nicht zu benutzen hat noch einen weiteren Vorteil: Refactorings werden leichter und man muss nicht mit Strings von Methodennamen arbeiten.11.01.2012 um 10:25 Uhr
Bei den Templatemethoden geht es genau darum, dass sie nicht von einer erbenden Klasse implementiert werden müssen wie das bei der Verwendung von Interfaces oder abstrakten Klassen der Fall wäre.
Dadurch lassen sich z.B. Hooks in Softwareprojekten umsetzen, wobei diese innerhalb einer bestehenden Logik immer aufgerufen werden und es dadurch ist, diese einfach durch eigenen Code zu erweitern.
11.01.2012 um 11:42 Uhr
Es empfiehlt sich trotzdem, ein Interface zu nutzen, welches diese Methode vorgibt. Erstens, man kann relativ einfach Mocken in Tests. Zweitens wird sichergestellt, das es die Methode gibt (erspart mit Type Hinting den method_exists Check). Drittens, eine Abstrakte Basis-Klasse kann die Methode (leer) implementieren.
11.01.2012 um 11:53 Uhr
Schaden wird es sicher nicht.
Wie schon gesagt geht es ja darum, dass die aufrufende Klasse diese (leere) Methode immer schon mitbringt, bzw. in diesen Beispielen wie sich die verschiedenen Aufrufe mit/ohne Prüfung auf die Verarbeitungszeit auswirken.