monofone

coding.

Turning into Greenfield II

Nachdem geklärt ist, dass alle gemeinsam ein Ziel haben und die Bedingungen dafür geschaffen wurden, kann mit der Technik begonnen werden. Es muss dabei immer im Hinterkopf behalten werden das dies ein längerer Prozess ist. Wenn das Produkt laufend Erweitert werden muss kann es sich dabei um Jahre handeln.

Wenn man als Grundlage zu großen Teilen PHP hat und diese Quellen in einem gewachsenen Zustand vorliegen, dann muss ein Weg gefunden werden hier aufzuräumen.

Von innen nach außen

Bei prozedural orientiertem Code mit vielen festen Abhängigkeiten ist das Ziel das Aufbrechen dieser Abhängigkeiten und eine losere und objetkbezogene Kopplung herzustellen. Dieser Ansatz hat zwei Ziele. Das erste Ziel ist die bessere Übersichtlichkeit der Abhängigkeiten, das 2. Ziel ist die Grundlagen zur Einführung von Unittests zu schaffen. Gleichzeitig kann die Gelegenheit genutzt werden, die Architektur an Design Patterns anzupassen. Es sollte jedoch nicht unterschäzt werden welchen Aufwand dies bedeutet. Bei Code mit gewachsenen Strukturen sind Seiteneffekte, die durch das Refactoring entstehen, zwangsläufig und dies kann nur durch aufwendige Funktionsstest abgefangen werden.

In Büchern zu diesem Thema wird gerne auf das anfängliche Einführen von Tests hingewiesen. Dies ist jedoch nicht immer möglich. Für diesen Fall gibt es jedoch einen anderen Weg.

Bei Neuentwicklungen innerhalb des Projektes müssen ab nun von begin an Designpatterns berücksichtigt werden. Insbesondere die Ideen zur losen Kopplung, um Unittests schreiben zu können, sollten dabei berücksichtigt werden. Lässt es die Projektstruktur zu, sollten neue Entwicklungen in einem eigenen Bereich vorgenommen werden, so dass es später möglich ist klar zwischen alten und neuen Strukturen unterscheiden zu können.

Für PHP bieten sich die Symfony Components an. Diese lassen sich über PEAR einfach installieren und verwalten. Dazu kommen schöne Features und gut durchdachtes Design. Als erstes sollte der ClassLoader eingeführt werden, wenn es so etwas noch nicht gibt.

Dazu legt man sich eine Autoload Datei an, in der man das Initalisieren des Autoloadings vornimmt.

Includes sind sehr aufwendig zu verwalten wenn einmal die Pfade bzw. der Ort an dem die Sourcen liegen geändert werden soll. Dies kann z.B. der Fall sein wenn man Backendcode aus dem Documentroot entfernen möchte. 

Für diesen Fall gibt es den  MapClassLoader der sich ebenfalls in der Komponente Classloader befindet. Dieser nimmt einfach ein Array aus Klassennamen und Pfaden entgegen.

//autoload.php
//...
include_once '/pearPath/Symfony/Component/ClassLoader/MapClassLoader.php';
use Symfony\Component\ClassLoader\MapClassLoader;
$map = array(
'MyClass' => __DIR__.'/path/to/myClass.class.php', ); 
$loader = new MapClassloader($map);
$loader->register();

Jetzt braucht nur das autoload.php eingebunden werden und es ist für alles neu Entwickelte kein include oder require mehr notwendig. Dies war nun der erste Schritt um mehr Ordnung in die Codebase zu bringen.

Mit diesem Setup als Grundlage können nun weitere Strukturanpassungen vorgenommen werden. Da bei der Entwicklung von neuen Komponenten ja Rücksicht auf lose Kopplung und Constructor oder Setter Injection genommen wurde ist es nun kompliziert diese neuen Komponenten zu Instanzieren. Deswegen macht ein Dependency Injection Container Sinn. Dieser wird durch die Komponente DepencyInjection von Symfony bereit gestellt.

pear install --alldeps symfony2/DependencyInjection

Um den Container zu initialisieren lohnt es sich ebenfalls eine eigene Datei anzulegen.

Nun geht es darum die Services zu konfigurieren. Der Übersichthalber bevorzuge ich XML.

Das XML beschreibt mehrere Dinge auf einmal. Zum einen ist da der Import. Der ermöglicht es andere Dateien in die Konfiguration einzubeziehen. In diesem Fall eine PHP-Datei die dazu dient bereits vorhandene Konfigurationsdateien in den Container zu integerieren. Dies ermöglicht einem den Prallelbetrieb der Anwendung mit der vorhandene Konfiguration und Nutzung der Parameter im Container.

Parameters sind dann einfach. Optionen und Konfigurationen die im Conatainer zur Verfügung stehen. Dies können an anderer Stelle durch de % Zeichen referenziert werden.

Der letzte Block services ist dann das Konfigurieren der Services. In diesem Fall werden 2 Services Definiert. Eine etwas komplexere Definition über einen Factory, dies ist praktisch im zusammenhang mit dem erzuegen von Resourcen wie z.B. einem MySQL link o.ä.

Ab nun ist es möglich innerhalb seiner Anwendung auf den Container zuzugreifen. Im legacy Code über die das etwas unglückliche Konstrukt.

$GLOBALS['container']->get('my_service);

Beim nächsten Mal ist es dann hoffentlich Zeit für ein Event.