Hi!
Scheitert, wenn der Original-Code wegen OpCode-Compilern [...] nicht geändert werden kann.
Das verstehe ich leider nicht. Wie meinst Du das mit den OpCode-Compilern? In welcher Form spielen die eine Rolle für die Erweiterung des Original-Codes?
War vielleicht etwas ungünstig formuliert. Der Original-Code ist der, den du schreibst. Wenn man den nicht offenlegen will, liefert man an Kunden vorkompilierten "Byte-Müll". Den kann der Kunde dann zwar verwenden, aber ihn nicht mehr ändern. Dan kann man natürlich als Kunde auch keine Erweiterungen mehr einpflegen.
Warum erschwert eine Factory das Testen?
Über eine Factory kann man im Allgemeinen nur aus einer feststehende Anzahl an "Produkten" wählen. Man kann ihr keine Mock-Objekte unterschieben. Vielmehr müsste man dann eine Test-Klasse schreiben, die sich in die Reihe der Produkte eingliedert. Damit ist man deutlich unflexibler als mit Mocks. So ein Mock definiert man ja on-the-fly innerhalb der Test-Methode - und in jeder Test-Methode mit unterschiedlichen Parametern und Reaktionen. Das kannst du mit einer Test-Produkt-Klasse kaum schaffen.
Muss das ganze TDD-fähig sein? [...]
Ich wusste bis gerade nicht, was TDD bedeutet (für alle Unwissenden wie mich: Testgetriebene Entwicklung).
Ausgeschrieben: Test Driven Development. Man schreibt sich zunächst für alle denkbaren und alle auszuschließenden Anwendungsfälle jeweils eine Methode, die diesen Anwendungsfall testet. Vom Testobjekt hat man zunächst nicht mehr als ein Gerippe - also Eigenschaften sind da, Methoden sind aber alle leer. Dann lässt man den Test zum ersten Mal laufen und stellt fest, dass alle Tests fehlschlagen. Sehr gut - nicht dass der Test an sich falsch implementiert ist und einfach so ein positives Ergebnis erzeugt. Nun füllt man das Gerippe mit Leben und schaut, wie der Test immer grüner wird. Für TDD gibt es Frameworks, die einen beim Schreiben solcher Tests unterstützen. Und die schon erwähnten Mocks sind dabei Helfer, die so tun, als seien sie Instanzen eines bestimmten Interfaces, das vom zu testenden Objekt verwendet wird.
Also, die Config-Klasse soll getestet werden. Die will per DI ein Objekt einer Klasse mit implementiertem Quellen-Interface haben, aus dem sie sich die Konfigurationswerte holen kann. Man nimmt für den Test nun keine Test-Klasse sondern ein Mock-Objekt. Das tut so, als hätte es das Quellen-Interface implementiert und wird so initialisiert, dass beim Aufruf einer der im Interface vereinbarten Methoden ein bestimmter Wert zurückgegeben wird. Nun veranlasst man die Config-Klasse, dass sie etwas macht, das genau diese Methode im angeblichen Quellen-Objekt aufruft und kann dann schauen, wie die Konfig-Klasse auf unterschiedliche vom Mock-Objekt bereitgestellte Rückgabewerte reagiert. Hört sich komplex an? Isses auch. Aber es gibt nicht nur Test-Frameworks sondern auch fertige Mock-Implementationen. (Wobei ich aber für PHP keine aktuelle Marktübersicht habe.)
Komplexer und komplizierter geht immer. Zum Beispiel könnte man statt eines (verschachtelten) Arrays in der Config-Klasse weiteren Klassen vorsehen, die im einfachsten Fall einen Wert aber auch andere individuelle Strukturen enthalten können.
Wenn ich Dich richtig verstehe, versuchen wir das bereits. Ich hab mal eine Zip-Datei mit einer anfänglichen und unfertigen (was für ein tolles Wort...) Version einer möglichen Config-Implementierung hochgeladen.
Darin sehe ich von meinem Beispiel nichts. Im Moment legst du die Werte direkt als neue Eigenschaften im Config-Objekt an. Soweit so einfach. Ich meinte das komplizierter. Es gibt eine Klasse, die im einfachsten Fall einen Wert speichert. Aber es kann auch Klassen geben, die komplexere Strukturen aufnehmen kann. Beide implementieren aber das selbe Interface und dein Config-Objekt speichert nur Objekte dieses Interfaces. - So zumindest könnte man das in einer typsicheren Umgebung machen. Für PHP ist das ziemlicher Overkill. Hier kann ein Array-Element ganz einfach einen skalaren Wert und für Strukturen ein Array oder Objekt aufnehmen. Da ist man ja schon durch die flexible Typisierung flexibel genug.
(Vorschläge natürlich sehr erwünscht): http://fsac.de/dat/selfhtml/library.zip
Fsac\Object\ExtendedArrayObject.php
Kein toller Name. Kannst du den Namen so sprechend machen, dass man die hinzugefügte Funktionalität daraus entnehmen kann?
public function __construct($allowOverwrite = false, $throwExceptions = false) {
if (true === $allowOverwrite) {
$this->allowOverwrite();
Da spielt es doch überhaupt keine Rolle, ob man true oder 1 übergibt. Also muss da auch kein typsicherer Vergleich hin. Typsicher muss man es nur dann machen, wenn man zum Beispiel zwischen false und 0 unterscheiden muss.
protected final function ...
Was'n das? Warum nicht gleich private?
Fsac\Config\Config - addArray()
@todo What implementation is better?
+1 für den Einzeiler.
Dein ArrayObject implementiert Iterator und ist damit in der Verwendung sehr eingeschränkt, weil man immer nur einen zur gleichen Zeit laufen lassen kann. Mit IteratorInterface wird man da flexibler, weil das so gedacht ist, dass es einen eigenständigen Iterator liefert.
Und dann ist das ganze Gebilde ja Variante 1 und keine DI.
Lo!