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

Предположим, вы разделяете свои системы на объекты-ценности и объекты-службы (как это предлагается в статье «Развитие объектно-ориентированного программного обеспечения на основе тестов». Миско Хевери называет их «новыми» и «инъекционными»).

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

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

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

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

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

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

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


person WW.    schedule 25.06.2010    source источник


Ответы (3)


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

person expedient    schedule 25.06.2010
comment
Интересный. Мне кажется естественным, что поведение (проверка) должно жить с данными, а не с объектом CreditCard. - person WW.; 25.06.2010
comment
Однако это переворачивает реальный мир с ног на голову. Кредитная карта не имеет никакого способа проверить себя - поддельные карты с истекшим сроком действия живут в дикой природе и понятия не имеют, действительны они или нет. Если вы отправите запрос в платежную систему, они не будут спрашивать вас, действительна ли ваша карта или верить вам на слово — они определят это сами и просто запросят данные, необходимые для этого. - person expedient; 25.06.2010
comment
@WW: Роберт С. Мартин, дядя Боб, также говорит об этом, описывая принцип единственной ответственности SOLID в своем примере с модемом (6-страничная статья: objectmentor.com/resources/articles/srp.pdf). Я думаю, что в прошлом CreditCard.Validate() считался хорошим объектно-ориентированным дизайном, но тенденция, похоже, уходит от этого ко многим отдельным классам. Также я задал вопрос об этом на ProgrammersSE параметр" title="метод объекта дизайна и метод отдельных классов, который принимает объект в качестве параметра">programmers.stackexchange.com/questions/77690/ - person User; 02.07.2011

У меня возникло бы искушение сказать, что CreditCard не является объектом-значением.

Из вики C2:

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

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

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

У меня обычно есть объект-значение, сервис и бизнес-объект. Я не знаю, что такое «Развитие объектно-ориентированного программного обеспечения», но мне кажется странным ограничивать себя только ценными объектами и услугами.

person ewernli    schedule 25.06.2010
comment
Итак, если CreditCard является бизнес-объектом, как бы вы справились с ситуацией в вопросе? - person WW.; 25.06.2010
comment
@WW Я предлагаю вам взглянуть на дизайн, управляемый доменом. Об этом много ресурсов. Вот обсуждение DDD на примере кредитной карты misko.hevery.com/2009/03/16/ - person ewernli; 26.06.2010

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

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

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

person Rogério    schedule 28.03.2011