我一直在看Misko Hevery的Google clean code talks。这些讨论说:在构造函数中请求依赖,因此其他程序员可以预先确切地知道需要什么,来实例化给定对象的实例(law of demeter)。这也使得测试更容易,因为程序员确切地知道需要嘲笑什么。
示例时间
如果我有一个类Customer
,并且我还有一个CustomerDAO
类来抽象数据访问。当我构建客户对象时,我可能会执行以下操作:
database = new Database('dsn');
customerDao = new CustomerDAO(database);
customer = new Customer(customerDao);
这可能发生在我的控制器中。我可以通过使用依赖注入容器来简化此对象构造。下面我使用了一个DI容器来获取我的数据库类的实例,因为它在我的应用程序中被广泛使用。这会将构造代码减少到一个地方,并且可以进行模拟以进行测试。
我应该将我的域类依赖项(在本例中为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
,他们只需要模拟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;
}
}
问题
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容器中?
答案 0 :(得分:0)
如果我正确地解释这一点,看起来您的问题实际上是关于实体构建和生命周期管理。
DDD是一种设计方法,它为如何解决这些问题提供了非常规范的指导;在您的情况下,相关概念是存储库和聚合根。虽然DDD可能不会直接回答您的问题,但它会使您更容易想出符合您要求的基于模式的解决方案。
我故意不试图解释一般的DDD或我提到的概念;关于SO和其他地方可用的材料,有足够的材料。