Когда мне следует использовать Dependency Injection, а когда использовать служебные методы? - PullRequest
5 голосов
/ 03 мая 2010

У меня есть проект Java EE с контейнером Spring IoC.

Я только что нашел в Utils статический метод класса sendMail(long list of params). Я не знаю почему, но я чувствую, что это выглядело бы лучше, если бы у нас был отдельный класс (Spring bean с синглтоном), который будет отвечать за отправку электронной почты. Но я не могу найти аргументов, которые могли бы доказать мою позицию.

Итак, есть ли плюсы (или минусы) в использовании DI в этой (довольно общей) ситуации?

Ответы [ 6 ]

6 голосов
/ 03 мая 2010

Если классы, которым требуется отправлять электронную почту, использовали MailSender интерфейс (имя образца) вместо static Utils.sendMail(...) метода, то вы можете легко выполнить модульное тестирование этих классов, поменяв местную / другую реализацию MailSender в ваши юнит-тесты вместо того, чтобы использовать реальную реализацию.

Это позволяет вам проверить поведение этих классов при успешной отправке почты, при обнаружении исключения / ошибки и т. Д.

Если в классах используется статический метод, подобный тому, который вы описали, то вы не можете тестировать поведение этих классов изолированно, фактически не отправляя электронную почту - в этот момент это не эффективный, изолированный модульный тест, поскольку теперь вы будете зависеть от поведения 1) кода отправки почты 2) сервера исходящей почты и т. д.

5 голосов
/ 03 мая 2010

Да! Я приведу пример прямо из моего недавнего опыта.

Недавно мы переключили кучу приложений с отправки электронной почты напрямую на использование очереди базы данных для отправки сообщений. Сообщения помещаются в очередь в БД, а затем отправляются через пакетный процесс. Это дает нам возможность обрабатывать сбои SMTP-сервера, пересылать сообщения, контролировать, какие сообщения можно отправлять с нашего сервера разработки, проверять, действительно ли сообщения были отправлены и т. Д.

Что-то такое простое, как отправка электронного письма, может, безусловно, быть тем, что вы захотите изменить в будущем. Внедрение реализации сделало бы это намного проще.

4 голосов
/ 03 мая 2010

Классы типа Util, которые содержат путаницу обычно статических функций, обычно не являются хорошими вещами. Я даже видел такой класс с именем UglyGlobals в одном проекте, над которым я работал. По крайней мере, они были честны об этом! Как правило, вы правы. Нечто подобное рассылке является отличным кандидатом для превращения в одноэлементный компонент, который вводится.

0 голосов
/ 03 мая 2010

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

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

Если вы внедрите интерфейс, представляющий MailSender, вы можете предоставить NoopMailSender, который ничего не делает, если вам неважно отправляемое письмо, и StubMailSender фальшивка, которая собирает List<EmailMessage> писем, были отправлены через него, если вы хотите проверить, были ли определенные письма отправлены вашим кодом.

0 голосов
/ 03 мая 2010

DI позволяет легко смоделировать вашу реализацию для написания тестов. Например, предположим, что вы хотите проверить поток сброса пароля. С жестко запрограммированным Utils.sendMail () ваш тестовый код будет вынужден создать поддельный SMTP-сервер, прочитать и проанализировать электронную почту, а затем нажать на ссылку для сброса пароля. Если бы вы использовали DI, вы могли бы передать фиктивный Emailer объект. Таким образом, вы можете писать супер-быстрые модульные тесты, не заботясь о внешних интеграциях.

Вы также можете легко менять реализации. Например, Google App Engine имеет собственный API sendMail - поэтому вам будет трудно одновременно поддерживать версию GAE с кодом и не-GAE. (Ради аргумента, просто предположите, что переход на GAE - это так просто. Это не так, очевидно).

Наконец, ваш код более модульный. Определенный класс (ResetPassword) может зависеть только от Util.sendMail (), но Util может быть кухонной раковиной с методами, позволяющими делать все под солнцем. Итак, если вы хотите повторно использовать ResetPassword в другом проекте, вам необходимо скопировать весь класс Utils, а также несколько зависимых jar-файлов, которые Utils должен работать. Это не хороший вариант.

0 голосов
/ 03 мая 2010

Весенняя почта

Сначала я бы посоветовал вам взглянуть на пакет org.springframework.mail . Он предоставляет полезную служебную библиотеку для отправки электронной почты и служит абстракцией для скрытия специфики базовых почтовых систем.

И так как вы уже используете spring, использование этой библиотеки безболезненно, если она предоставляет все, что вам нужно сделать с приложением в отношении отправки почты.

Статические методы в сравнении с инъекцией зависимостей

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

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

...