Ветвление кода без исключений

Использование исключений для управления потоком выполнения является достаточно распространённой практикой. Однако во многих статьях и книгах, посвящённым лучшим практикам, например в замечательной книге Джошуа Блоха “Java — Эффективное программирование“ (Effective Java, Joshua Bloch), даётся рекомендация не использовать исключения как способ ветвления кода.

Читать далее Ветвление кода без исключений

Гексагональная архитектура, и как я к ней пришёл

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

Читать далее Гексагональная архитектура, и как я к ней пришёл

Инверсия управления: внедрение и поиск зависимостей

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

Читать далее Инверсия управления: внедрение и поиск зависимостей

Многоуровневая архитектура в проекте на Java (Часть 2)

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

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

Читать далее Многоуровневая архитектура в проекте на Java (Часть 2)

Многоуровневая архитектура в проекте на Java (Часть 1)

В настоящее время в разработке ПО достаточно часто применяется многоуровневая архитектура или многослойная архитектура (n-tier architecture), в рамках которой компоненты проекта разделяются на уровни (или слои). Классическое приложение с многоуровневой архитектурой, чаще всего, состоит из 3 или 4 уровней, хотя их может быть и больше, учитывая возможность разделения некоторых уровней на подуровни. Одним из примеров многоуровневой архитектуры является предметно-ориентированное проектирование (Domain-driven Design, DDD), где основное внимание сконцентрировано на предметном уровне.

Читать далее Многоуровневая архитектура в проекте на Java (Часть 1)

SOLID на практике — Принцип подстановки Барбары Лисков

Принцип подстановки [Барбары] Лисков (Liskov Substitution PrincipleLSP, буква L в аббревиатуре SOLID), сформулирован Барбарой Лисков в 1987 году и звучит следующим образом:

Пусть q(x) является свойством, верным относительно объектов x некоторого типа T. Тогда q(y) также должно быть верным для объектов y типа S, где S является подтипом типа T.

Упрощенное описание этого принципа предложил Роберт Мартин:

Функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не зная об этом.

Иными словами, поведение реализующих и наследующих классов не должно противоречить поведению базовых типов.

Читать далее SOLID на практике — Принцип подстановки Барбары Лисков

SOLID на практике — принцип инверсии зависимостей

Принцип инверсии зависимостей (Dependency Inversion PrincipleDIP, буква D в аббревиатуре SOLID), описанный Робертом Мартином, состоит из двух постулатов:

  • Высокоуровневые модули не должны зависеть от низкоуровневых; и те и другие должны зависеть от абстракций
  • Абстракции не должны зависеть от деталей, детали должны зависеть от абстракций

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

  • Взаимодействие между классами должно быть реализовано через интерфейсы или абстрактные классы
  • Типами всех членов классов должны быть интерфейсы или абстрактные классы
  • Классы, являющиеся конечными реализациями не должны расширяться (или должны быть финальными)
  • Аналогично методы не должны перекрываться при наследовании (или быть финальными)

Читать далее SOLID на практике — принцип инверсии зависимостей

SOLID на практике — принцип единственной ответственности

Принцип единственной ответственности (Single Responsibility Principle — SRP, буква S в аббревиатуре SOLID), описанный Робертом Мартином, гласит: «Класс должен иметь только одну причину для изменения».

Обратите внимание на следующий код:

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

Следуя логике принципа единственной ответственности, нам следует выделить работу с источником данных, валидацию и проверку прав доступа, как минимум, в отдельные методы, а наиболее правильно — в отдельные классы.

Выделение логики в отдельные методы

В первом варианте мы разделим логику между методами класса TopicService. Кроме этого данные, получаемые методом update, логично будет инкапсулировать в одном классе:

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

Теперь этот код на уровне методов соответствует принципу единственной ответственности, чего не скажешь о всём классе TopicService. Не смотря на то, что методы findOneById, save, checkAccess и validate скрыты от внешнего наблюдателя, класс всё равно выполняет много действий.

Однако даже при этом код уже более удобен для понимания, а так же пригоден для повторного использования.

Выделение логики в отдельные классы

При выделении логики валидации, проверки прав и работы с источниками данных в отдельные классы, код приложения станет ещё удобнее и понятнее.

Таким образом мы получим класс, работающий с источником данных:

Класс, выполняющий валидацию UpdateTopicAction:

Класс, проверяющий доступ пользователя к объекту Topic:

Итоговый вид нашего сервиса будет следующим:

Таким образом мы на практике применили принцип единой ответственности.