Die Frage nach static und interface hängen zusammen. Grundsätzlich kann man den Prozeduralen Ansatz wählen und alles, was nicht mehrfach als Objekt instanziiert werden muss, als Klasse mit statischen Elementen realisieren. Die Klasse wird dann zum Namespace degradiert. Wenn die Klasse Hinz aus der Klasse Kunz die Methode Foo braucht, ruft sie eben Kunz::Foo() auf und gut ist.
Problem sind hier die Abhängigkeiten, die das schafft. Wenn du die Klasse Hinz testen willst, muss Kunz immer mit an Bord sein. Das ist ein Problem, vor allem dann, wenn Kunz::Foo() dazu da ist, eine komplexere SQL-Abfrage zu kapseln. Tests funktionieren dann nur mit definierten Datenbankinhalten und brauchen ewig. Aber eigentlich sollte der Test ja nur zeigen, dass die Methode Hinz->Oaf() die von Kunz::Foo() gelieferten Daten ordentlich filtert. Das würde mit einer Simulation eines SQL Ergebnisses viel besser und schneller funktionieren, aber weil Kunz::Foo() in Hinz->Oaf() hardcoded ist, geht es nicht.
Viele Programme sehen so aus, viele Programmierer arbeiten so, und plärren dann: Unit Tests sind scheiße und sind überhaupt nicht baubar. Weiß ich genau, habe ich jahrelang selbst gemacht. Wenn man Programmieren mit direkten Abhängigkeiten gelernt hat, dauert es einige Zeit, bis der IoC Groschen fällt.
Deshalb: statische Elemente sind IMMER eine Problemquelle und sollten homöopathisch verwendet werden. Du kannst sie nicht mocken (sprich: dem Nutzer vortäuschen, er würde damit arbeiten, aber in Wahrheit verwendet er ein Surrogat) und du kannst Hinz auch nicht sagen, er soll jetzt statt Kunz bitte Krethi verwenden (na gut, kannst du, aber das macht in PHP mangels Metaklassen bzw. Type-Klasse wenig Spaß).
Viel einfacher ist es, wenn Kunz ein Interface IKunz implementiert (oder mehrere Interfaces, nach Funktionsgruppen). Auf IKunz sind alle Methoden deklariert, die Kunz öffentlich bereitstellt. Kleines Manko in PHP: Es gibt keine automatischen Property-Getter, und deshalb musst Du für öffentliche Eigenschaften, die auf dem Interface erscheinen sollen, get- und set-Methoden definieren (oder machst es wie jQuery: $kunz->bar() liefert das bar-Property und $kunz->bar(4711) schreibt einen Wert hinein). Wer ein Hinz-Objekt erzeugen will, muss dann zwangsweise eine Implementierung von IKunz mitliefern. Das nennt man Dependency Injection oder Inversion of Control: Hinz holt sich seine Abhängigkeiten nicht, sondern bekommt sie eingespritzt.
Natürlich ist das mühsam, wenn man das alles von Hand tun muss. Darum gibt es Tools, sogenannte IoC Container, die das per Reflection oder Config-File automatisch erledigen. Du sagst nicht "new Hinz(new Kunz()), sondern $ioc->resolve("Hinz")
. Was in PHP wieder mühsam ist. In C# oder Java hättest du generische Typen und Methoden, und würdest container->Resolve<IHinz>()
aufrufen. Wenn's Hinz nicht gibt, merkt das dann schon der Compiler. Ich habe mich selbst noch nicht mit IoC in PHP beschäftigt, nur in C#, aber ich weiß, dass es auch in PHP Container gibt.
Das ist alles Infrastruktur für zwei Zwecke: Auflösen von direkten Abhängigkeiten und Testbarkeit. Direkte Abhängigkeiten behindern Wiederverwendbarkeit, und Testbarkeit ist wichtig für die Softwarequalität. Das Konzept der Testgetriebenen Entwicklung (TDD) sagt, dass man erst die Tests schreibt und damit spezifiziert, welches Verhalten man von einer Klasse erwartet, und dann die Methoden baut. Wenn man etwas ändern will, ändert man erst die Tests, dass sie das gewünschte neue Verhalten abfragen, und erweitert dann die betroffenen Methoden. Das ist eine GANZ andere Denkweise als früher und es fällt zumindest mir sehr schwer, mich konsequent daran zu halten. Ich muss mich immer wieder treten, nicht "mal eben so" eine Methode zu updaten. TDD ist eine Wissenschaft für sich, aber wenn Du einen vollständigen Satz an Unit-Tests für dein Programm hast, dann bist Du bei Änderungen an der Implementierung relativ sicher, dass die von Dir erwarteten Verhaltensweisen deiner Methode unverändert geblieben sind. Das schafft eine ganz neue Form von Qualität. Konzepte wie Continous Deployment wären ohne umfangreiche automatisiertes Tests unmöglich. Und mit Testbarkeit durch Unit-Tests fängt das alles an.
Rolf