Die beiden Hauptgründe gegen die Verwendung statischer Methoden sind:
- Code mit statischen Methoden ist schwer zu testen
- Code mit statischen Methoden ist schwer zu erweitern
Ein statischer Methodenaufruf in einer anderen Methode ist tatsächlich schlimmer als das Importieren einer globalen Variablen. In PHP sind Klassen globale Symbole. Jedes Mal, wenn Sie eine statische Methode aufrufen, verlassen Sie sich auf ein globales Symbol (den Klassennamen). Dies ist ein Fall, wenn global böse ist. Ich hatte Probleme mit dieser Art von Ansatz mit einer Komponente von Zend Framework. Es gibt Klassen, die statische Methodenaufrufe (Fabriken) verwenden, um Objekte zu erstellen. Es war mir unmöglich, dieser Instanz eine andere Fabrik zu liefern, um ein benutzerdefiniertes Objekt zurückzugeben. Die Lösung für dieses Problem besteht darin, zu Beginn des Programms nur Instanzen und Instace-Methoden zu verwenden und Singletons und dergleichen zu erzwingen.
Miško Hevery , der als Agile Coach bei Google arbeitet, hat eine interessante Theorie oder rät eher, die Objekterstellungszeit von der Zeit zu trennen, in der wir das Objekt verwenden. Der Lebenszyklus eines Programms ist also zweigeteilt. Der erste Teil ( main()
sagen wir die Methode), der sich um die gesamte Objektverdrahtung in Ihrer Anwendung kümmert, und der Teil, der die eigentliche Arbeit erledigt.
Also anstatt zu haben:
class HttpClient
{
public function request()
{
return HttpResponse::build();
}
}
Wir sollten lieber tun:
class HttpClient
{
private $httpResponseFactory;
public function __construct($httpResponseFactory)
{
$this->httpResponseFactory = $httpResponseFactory;
}
public function request()
{
return $this->httpResponseFactory->build();
}
}
Und dann würden wir auf der Index- / Hauptseite Folgendes tun (dies ist der Schritt der Objektverdrahtung oder die Zeit, um das Diagramm der vom Programm zu verwendenden Instanzen zu erstellen):
$httpResponseFactory = new HttpResponseFactory;
$httpClient = new HttpClient($httpResponseFactory);
$httpResponse = $httpClient->request();
Die Hauptidee besteht darin, die Abhängigkeiten von Ihren Klassen zu entkoppeln. Auf diese Weise ist der Code viel erweiterbarer und, was für mich am wichtigsten ist, testbar. Warum ist es wichtiger, testbar zu sein? Da ich nicht immer Bibliothekscode schreibe, ist die Erweiterbarkeit nicht so wichtig, aber die Testbarkeit ist wichtig, wenn ich Refactoring durchführe. Wie auch immer, testbarer Code liefert normalerweise erweiterbaren Code, so dass es sich nicht wirklich um eine Entweder-Oder-Situation handelt.
Miško Hevery unterscheidet auch klar zwischen Singletons und Singletons (mit oder ohne Großbuchstaben S). Der Unterschied ist sehr einfach. Singletons mit Kleinbuchstaben "s" werden durch die Verkabelung im Index / Main erzwungen. Sie instanziieren ein Objekt einer Klasse, die das Singleton-Muster nicht implementiert, und achten darauf, dass Sie diese Instanz nur an eine andere Instanz übergeben, die sie benötigt. Andererseits ist Singleton mit einem Großbuchstaben "S" eine Implementierung des klassischen (Anti) Musters. Grundsätzlich eine globale Verkleidung, die in der PHP-Welt wenig Verwendung findet. Ich habe bis jetzt noch keinen gesehen. Wenn Sie möchten, dass eine einzelne DB-Verbindung von allen Klassen verwendet wird, gehen Sie wie folgt vor:
$db = new DbConnection;
$users = new UserCollection($db);
$posts = new PostCollection($db);
$comments = new CommentsCollection($db);
Wenn wir das oben genannte tun, ist es klar, dass wir einen Singleton haben und wir haben auch eine gute Möglichkeit, einen Mock oder einen Stub in unsere Tests zu injizieren. Es ist überraschend, wie Unit-Tests zu einem besseren Design führen. Aber es ist sehr sinnvoll, wenn Sie glauben, dass Tests Sie dazu zwingen, über die Art und Weise nachzudenken, wie Sie diesen Code verwenden würden.
/**
* An example of a test using PHPUnit. The point is to see how easy it is to
* pass the UserCollection constructor an alternative implementation of
* DbCollection.
*/
class UserCollection extends PHPUnit_Framework_TestCase
{
public function testGetAllComments()
{
$mockedMethods = array('query');
$dbMock = $this->getMock('DbConnection', $mockedMethods);
$dbMock->expects($this->any())
->method('query')
->will($this->returnValue(array('John', 'George')));
$userCollection = new UserCollection($dbMock);
$allUsers = $userCollection->getAll();
$this->assertEquals(array('John', 'George'), $allUsers);
}
}
Die einzige Situation, in der ich statische Elemente verwenden würde (und ich habe sie verwendet, um das JavaScript-Prototypobjekt in PHP 5.3 nachzuahmen), ist, wenn ich weiß, dass das jeweilige Feld instanzübergreifend denselben Wert hat. Zu diesem Zeitpunkt können Sie eine statische Eigenschaft und möglicherweise ein Paar statischer Getter / Setter-Methoden verwenden. Vergessen Sie jedoch nicht, die Möglichkeit hinzuzufügen, das statische Element mit einem Instanzmitglied zu überschreiben. Beispielsweise verwendete Zend Framework eine statische Eigenschaft, um den Namen der DB-Adapterklasse anzugeben, die in Instanzen von verwendet wird Zend_Db_Table
. Es ist schon eine Weile her, seit ich sie benutzt habe, so dass es vielleicht nicht mehr relevant ist, aber so erinnere ich mich daran.
Statische Methoden, die sich nicht mit statischen Eigenschaften befassen, sollten Funktionen sein. PHP hat Funktionen und wir sollten sie verwenden.