TemplateMethod vs. method_exists()-Prüfung

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.

4 Kommentare

  1. 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.

  2. 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.

  3. 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.

  4. 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.

Schlagwörter: Adapter, Amazon, Animation, Annotations, Anonyme Klasse, Ant, Apache, API, Array, ArrayAccess, Attachment, AutoLoader, Bedienung, Bedingung, Benchmark, Bildbearbeitung, BOM, Bootstrap, Bot, Byte Order Mark, Callback, CamelCase, Canvas, Captcha, Cheatsheet, CLI, Closure, Cloud, CodeSniffer, Community, Comparator, Contest, Controller, Converter, CouchDB, Countable, Cronjob, CSV, CustomLibrary, Custom_Model, Data Mapper, Datei, Datenbank, Datenstruktur, Datentypen, Dating, Decorator, Dekorierer, Design Patterns, Dump, Duplikat, each, Eclipse, Entwicklung, Entwurfsmuster, Enum, Erweiterung, Eventhandling, Exception-Handling, Extension, Factory, Fehler, Flash, Foreach, Formatierung, Formular, Funktion, Futon, Header, HTML5, HTTP, IDE, If, Implementierung, InnoDB, Interceptor, Interface, isset, Iterator, Java, JavaScript, jQuery, Konfiguration, Konsole, Kontrollstruktur, kopieren, Late Static Binding, Layout, Linux, Listeners, Logging, Löschen, Magento, Magic Methods, Marketing, Methode, Model, MVC, MySQL, NetBeans, Objekt, Observable, Observer, OOP, Operator, Parameter, Partnersuche, Performance, PHP, phpMyAdmin, PHPUnit, Plugin, Proxy, Qualitätssicherung, Query, Reflection, Request, Response, Rest-API, Rockstar, Routing, S3, Samba, Scheifen, Schleife, Schutz, Secure Shell, Selbstreferenz, Shop, Sicherheit, Sicherung, Singleton Pattern, SOAP, Sortierung, Sourcecode, Spam, Speicherproblem, Spickzettel, SPL, SSH, Statement, Stellvertreter, Strategy Pattern, Stream, String, Sun VirtualBox, Support, Switch, Symfony, Symfony2, Symfony Live, Tag, Template, Template Method, Ternär Operator, Testing, Thumbnail, Tool, Tour, Twig, Type-Cast, Umwandlung, Underscore, unset, Vererbung, Verzweigung, Video, Videospiel, Virtualisierung, Visitor Pattern, Vorschaubild, walk, Webserver, Webservice, Weiterleitung, Wrapper, Youtube, Zeitsteuerung, Zend Framework, Zend_Cloud, Zend_CodeGenerator, Zend_Http_Client, Zend_Service, Zugriffsmethode