Где должен быть мой код конструкции объекта при соблюдении закона Деметры?

Я наблюдал за чистыми разговорами Google с Миско Хевери. В этих беседах говорится: запросите зависимости в конструкторе, чтобы другие программисты могли заранее увидеть, что именно нужно, для создания экземпляра данного объекта ( закон деметры). Это также облегчает тестирование, так как программист точно знает, что нужно проверять.

Пример времени
Если у меня есть класс 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;
  }

}

Проблемы

  1. Поскольку я не передал свой DI-контейнер моему Customer объект, он не может создавать пользовательские объекты, как показано выше, так как не имеет ссылки на DSN базы данных (и на самом деле не должен знать, как создавать пользователей)
  2. Создание собственных зависимостей делает этот код непроверенным, поскольку он конкретный, без швов для насмешек.
  3. Если я передам контейнер моей 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?

1 ответ

Если я правильно истолковываю это, кажется, что ваш вопрос на самом деле касается построения сущностей и управления жизненным циклом.

DDD - это один из подходов к проектированию, который дает очень предписывающее руководство о том, как подходить к таким проблемам; в вашем случае соответствующие понятия - это репозитории и совокупные корни. Хотя DDD, вероятно, не ответит на ваш вопрос напрямую, вам будет гораздо проще найти решение на основе шаблонов, соответствующее вашим требованиям.

Я намеренно не пытаюсь объяснить DDD вообще или концепции, которые я упомянул; Есть достаточно материалов о том, что доступно на SO и в других местах.

Другие вопросы по тегам