Внедрение зависимостей при создании класса также требует значений времени выполнения?

Предположим, вы разделяете свои системы на объекты Value и объекты Services (как это предлагается в "Растущем объектно-ориентированном программном обеспечении, управляемом тестами". Misko Hevery называет их "новыми" и "инъецируемыми").

Что происходит, когда одному из ваших объектов-значений внезапно требуется доступ к сервису для реализации его методов?

Допустим, у вас есть хороший простой объект Value. Он неизменен, содержит несколько битов информации и все. Допустим, мы используем это примерно так:

CreditCard card = new CreditCard("4111-1111-1111-1111", "07/10");
if (card.isValid())
{
  // do stuff
} 
else
{
  // don't do stuff
}

Все идет нормально. isValid() реализует алгоритм контрольной цифры на номере карты и возвращает истину / ложь.

Теперь, скажем, я хочу улучшить систему, сверяя дату истечения с текущим временем. Как бы вы предположили, что это делается без нарушения Paradim объекта Value / объекта службы? Я хотел бы, чтобы этот класс продолжал быть модульным тестируемым.

  • CreditCard теперь имеет зависимость, но из-за того, как он создан, его нельзя внедрить, поэтому внедрение зависимости отсутствует.
  • CreditCard класс не должен вызывать Singletons (я считаю, что глобальный доступ к Singleton - плохая практика)
  • Положить поведение на CreditCardVerificationService.validateCard() означает, что весь существующий код должен быть пересмотрен. Реализация isValid() просачивается.

Я знаю, что есть вещи, которые можно сделать, чтобы обойти это, но какой самый чистый путь?

3 ответа

Решение

Я бы сказал, что проверять что-либо не является задачей объекта CreditCard. Фабрика проверит контрольные цифры, чтобы убедиться, что она создает соответствующую карту, в то время как служба проверки будет проверять карту на срок действия / лимит $.

Я хотел бы сказать, что CreditCard не является объектом значения.

Из вики C2:

Примерами объектов-значений являются такие вещи, как числа, даты, денежные суммы и строки. Обычно это небольшие объекты, которые используются довольно широко. Их идентичность основана на их состоянии, а не на их объектной идентичности. Таким образом, вы можете иметь несколько копий одного концептуального объекта значения.

Объект-значение не является BusinessObject/ReferenceObject. BusinessObject / ReferenceObject - это то, что вы можете найти в мире, а Value Object - это показатель или описание чего-либо.

Если CreditCardNumber может быть объектом значения, CreditCard больше похоже на бизнес-объект, который содержит некоторую бизнес-логику, например, проверку.

У меня обычно есть Value Object, Service и Business Object. Я не знаю о "Растущем объектно-ориентированном программном обеспечении", но ограничение себя только Value Object и Service кажется мне странным.

Я бы позвонил CreditCard объект, а не объект значения, поскольку он, вероятно, будет постоянным и будет иметь уникальную идентичность.

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

Если реализация службы должна быть выбрана во время выполнения, тогда можно использовать локатор службы. Этот шаблон может обеспечить прямую поддержку для насмешек / подделок, без необходимости в специальном инструменте для насмешек. Использование структуры DI, поддерживающей внедрение в "новые" объекты, было бы другой альтернативой.

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