静的メソッドを使用しない主な2つの理由は次のとおりです。
- 静的メソッドを使用するコードはテストが難しい
- 静的メソッドを使用するコードは拡張が難しい
他のメソッド内で静的メソッドを呼び出すことは、実際にはグローバル変数をインポートするよりも悪いです。PHPでは、クラスはグローバルシンボルであるため、静的メソッドを呼び出すたびに、グローバルシンボル(クラス名)に依存します。これは、グローバルが悪である場合です。私は、Zend Frameworkの一部のコンポーネントでこのようなアプローチに問題がありました。オブジェクトを構築するために静的メソッド呼び出し(ファクトリー)を使用するクラスがあります。カスタマイズされたオブジェクトを返すために、そのインスタンスに別のファクトリを供給することは不可能でした。この問題の解決策は、インスタンスとインスタンスメソッドのみを使用し、プログラムの最初にシングルトンなどを適用することです。
MiškoHevery Googleのアジャイルコーチとして働いて、興味深い理論を持っている、というかむしろ我々はオブジェクトを使用する時間からオブジェクトの作成時間を分ける必要があることを、お勧めします。したがって、プログラムのライフサイクルは2つに分割されます。main()
アプリケーションのすべてのオブジェクトワイヤリングと実際の作業を行う部分を処理する最初の部分(メソッドは、言ってみましょう)。
だから、代わりに:
class HttpClient
{
public function request()
{
return HttpResponse::build();
}
}
私たちはむしろすべきです:
class HttpClient
{
private $httpResponseFactory;
public function __construct($httpResponseFactory)
{
$this->httpResponseFactory = $httpResponseFactory;
}
public function request()
{
return $this->httpResponseFactory->build();
}
}
次に、インデックス/メインページで実行します(これは、オブジェクトの配線手順、またはプログラムで使用されるインスタンスのグラフを作成する時間です)。
$httpResponseFactory = new HttpResponseFactory;
$httpClient = new HttpClient($httpResponseFactory);
$httpResponse = $httpClient->request();
主なアイデアは、クラスから依存関係を切り離すことです。このようにして、コードははるかに拡張可能であり、私にとって最も重要な部分はテスト可能です。テスト可能であることがなぜより重要なのですか?私は常にライブラリコードを書くわけではないので、拡張性はそれほど重要ではありませんが、リファクタリングを行うときはテスト容易性が重要です。とにかく、通常、テスト可能なコードは拡張可能なコードを生成するため、実際にはどちらか一方の状況ではありません。
MiškoHeveryは、シングルトンとシングルトン(大文字のSの有無にかかわらず)を明確に区別しています。違いは非常に簡単です。小文字「s」のシングルトンは、インデックス/メインの配線によって強制されます。シングルトンパターンを実装していないクラスのオブジェクトをインスタンス化し、そのインスタンスを、それを必要とする他のインスタンスにのみ渡すように注意します。一方、大文字の「S」を持つシングルトンは、古典的な(アンチ)パターンの実装です。基本的に、PHPの世界ではあまり使用されていない、変装したグローバルです。今まで見たことがありません。すべてのクラスで単一のDB接続を使用したい場合は、次のようにすることをお勧めします。
$db = new DbConnection;
$users = new UserCollection($db);
$posts = new PostCollection($db);
$comments = new CommentsCollection($db);
上記を実行することにより、シングルトンがあり、モックまたはスタブをテストに挿入するための優れた方法があることは明らかです。驚くべきことに、単体テストがより良い設計につながる方法です。しかし、テストによってそのコードの使用方法を考えるように強いられると考える場合、それは非常に理にかなっています。
/**
* 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);
}
}
私が使用する唯一の状況(そして、PHP 5.3でJavaScriptプロトタイプオブジェクトを模倣するためにそれらを使用した場合)の静的メンバーは、それぞれのフィールドが同じ値のクロスインスタンスを持つことを知っている場合です。その時点で、静的プロパティを使用できます。また、静的ゲッター/セッターメソッドのペアを使用することもできます。とにかく、インスタンスメンバーで静的メンバーをオーバーライドする可能性を追加することを忘れないでください。たとえば、Zend Frameworkはのインスタンスで使用されるDBアダプタクラスの名前を指定するために静的プロパティを使用していましたZend_Db_Table
。私がそれらを使用してからしばらく経っているので、もはや関連性がないかもしれませんが、それは私がそれを覚えている方法です。
静的プロパティを扱わない静的メソッドは関数でなければなりません。PHPには関数があり、それらを使用する必要があります。