Misko Hevery によるGoogle のクリーン コード トークを見てきました。これらの話は次のように述べています: コンストラクターで依存関係を要求して、他のプログラマーが特定のオブジェクトのインスタンスをインスタンス化するために必要なものを事前に正確に確認できるようにします (デメテルの法則)。これにより、プログラマーは何をモックする必要があるかを正確に把握できるため、テストが容易になります。
例の時間
クラスがあり、データ アクセスを抽象化Customer
するクラスもあるとします。CustomerDAO
顧客オブジェクトを作成するときは、次のようにします。
database = new Database('dsn');
customerDao = new CustomerDAO(database);
customer = new Customer(customerDao);
これは私のコントローラーで発生する可能性があります。依存性注入コンテナを使用することで、このオブジェクトの構築を簡素化できます。以下では、DI コンテナーを使用してデータベース クラスのインスタンスを取得しました。これは、アプリケーション全体で広く使用されているためです。これにより、構築コードが 1 か所に減り、テスト用にモックできます。
ドメイン クラスの依存関係 (この場合は DAO オブジェクト) を DI コンテナーに追加する必要がありますか? アプリケーションが大きい場合、DI コンテナーは巨大になりますか?
DI コンテナーを使用すると、コードは次のようになります。
// container setup
container->dsn = '...';
container->dbh = function($c) {
return new Database($c->dsn);
};
container->customerDao = function($c) {
return new CustomerDAO($c->dbh);
};
// controller code
class ControllerCustomer extends ControllerBase {
public function index() {
container = this->getContainer();
customer = new Customer(container->customerDao);
view->customerName = customer->getName();
view->render();
}
}
別のプログラマーが をテストしたい場合はCustomer
、 mock だけが必要CustomerDAO
です。
この例をさらに一歩進めると、他のドメイン クラスに依存するドメイン クラスがある場合、私の DI コンテナーはすべてのドメイン クラスを構築する方法を知る必要がないはずです。例えば:
私の顧客は会社/機関であり、したがって多くのユーザーがいます。
class Customer {
protected _dao;
public function Customer(dao) {
_dao = dao;
}
public function listUsers() {
userIds = _dao->getAllUserIds();
users = array();
foreach (userIds as uid) {
user = new User(new UserDAO(new Database('dsn')); // problem
users[] user->load(uid);
}
return users;
}
}
問題
- DI コンテナーをオブジェクトに渡してい
Customer
ないため、データベース DSN への参照がないため、上記のようにユーザー オブジェクトを作成できません (ユーザーの作成方法を知る必要はありません)。 - 独自の依存関係を作成すると、モック用の継ぎ目がなく具体的であるため、このコードをテストできなくなります。
- コンテナを自分の
Customer
クラスに渡すと、インターフェイスがCustomer
嘘になりますか? (リンクされた Google ビデオの 9:15 を参照)。
オブジェクトCustomer
を構築できるようにするには、ユーザー ファクトリを渡す必要がありますか?User
database = new Database('dsn');
userDao = new UserDAO(database);
userFactory = new UserFactory(userDao);
customer = new Customer(customerDao, userFactory);
の構造をUserFactory
DI コンテナに入れる必要がありますか?