Я наблюдал за чистыми кодами Google от Misko Hevery. В этих беседах говорится: запросите зависимости в конструкторе, чтобы другие программисты могли заранее увидеть, что именно нужно, для создания экземпляра данного объекта ( закон деметра ). Это также облегчает тестирование, так как программист точно знает, что нужно смоделировать.
Пример времени
Если у меня есть класс Customer
, и у меня также есть класс CustomerDAO
для абстрагирования доступа к данным. Когда я создаю объект customer, я могу сделать следующее:
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;
}
}
Проблемы
- Так как я не передал свой DI-контейнер моему
Customer
-объекту, он не может создавать пользовательские объекты, как показано выше, так как не имеет ссылки на DSN базы данных (и на самом деле не должен знать, как это сделать). пользователей)
- Создание собственных зависимостей делает этот код непроверенным, поскольку он конкретный, без швов для насмешек.
- Если я передам контейнер моему классу
Customer
, это делает мой интерфейс для Customer
ложью? (См. 9:15 в связанном видео Google).
Должен ли я передавать фабрику пользователей в Customer
, чтобы она могла создавать User
объекты?
database = new Database('dsn');
userDao = new UserDAO(database);
userFactory = new UserFactory(userDao);
customer = new Customer(customerDao, userFactory);
Должна ли конструкция для UserFactory
находиться в моем контейнере DI?