Fluently self-contained Factory Method

Nein, wir spielen nicht Buzzword-Bingo. Heute geht es darum, Objekt-Instanzen durch einfache Factory Methoden zu erzeugen und sie im Weiteren als Fluent Interface direkt zu nutzen, ohne den Stolperstein “new” und ob das in der Form überhaupt sinnvoll ist.

Der herkömmliche Weg eine Instanz einer Klasse zu erzeugen ist die Verwendung des new-Operators.

<?php
$meinObject = new MeineKlasse();
$meineKlasse->machWas();

Leider funktioniert Methodenverkettung in PHP nicht wie in Java direkt nach “new” und wir müssen uns mit einer statischen Methode behelfen. Das Prinzip ähnelt dem Singleton-Muster, jedoch wollen wir in diesem Fall bei jedem Aufruf eine neue Instanz bekommen. Wer schonmal mit (Borland-)Delphi gearbeitet hat, der kennt das sicher schon.

<?php
class MeineKlasse
{
  public static function create()
  {
    return new self();
  }
}

Der Vorteil ist nun ganz klar der, dass sich Instanzen in einer Zeile benutzen lassen.

<?php
MeineKlasse::create()->machWas();

Leider fallen mir aber auch spontan zwei Nachteile ein: Bis PHP 5.3 ist die Methode nicht vererbbar, das bedeutet, dass wir die Methode mit jeder Ableitung erneut überschreiben müssten. Ab Version 5.3 kann statt “self” dann “static” instanziiert werden. Genaueres findet sich auch in den Links in meinem Singleton-Artikel.

Der zweite Nachteil ist genau der, warum man auch “new” nicht verwenden sollte. Beides schafft an vielen Stellen im Code Abhängigkeitet zu einer bestimmten Klasse. Wenn nun irgendwann eine abgeleitete Klasse benötigt wird müssen viele Änderungen gemacht werden.

Im Zweifelsfall sollte auf eine klassische, externe Factory zurückgegriffen werden, um derartigen Problemen im Voraus aus dem Weg zu gehen.

3 Kommentare

  1. Ein netter Ansatz, der mir auch schon hin und wieder die eine oder andere Code-Zeile erspart hat. Was man damit übrigens auch relativ elegant machen kann, ist unterschiedliche Konstruktoren (“Pseudo-Polymorphismus”) zu generieren, in etwa so:

    MeineKlasse::createWithA(A $a);
    MeineKlasse::createWithBC(B $b, C $c);
    MeineKlasse::createWithAC(A $a, C $c);

    Im Normalfall sollten so unterschiedliche Konstruktoren ja gar nicht notwendig sein, von Fall zu Fall könnte man sich damit aber einiges an Code (und vor allem an optionalen Konstruktor-Argumenten) sparen.

    Will man übrigens erzwingen, dass eine Klasse nur über diese create()-Methode instanziiert werden kann, so kann man ganz einfach den Konstruktor auf private oder protected setzen:

    protected function __construct() {}

  2. Wie wäre es statt “self” “__CLASS__” zu nutzen. Schau dir mal die Singleton Factories in symfony 1.4 z.B. bei sfContext an.
    In etwa:
    public function createInstance($class = __CLASS__)
    {
    return new $class();
    }

    soweit ich weiß macht das bei Vererbung keine Probleme und ist auch nochmal was flexibler.

  3. Mit __CLASS__ bekommst du nur den Namen der Klasse in der die jeweilige Methode definiert wurde. Hilft bei Vererbung dann leider auch nicht weiter.

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