- PVSM.RU - https://www.pvsm.ru -
Некоторым из этих книг уже очень много лет. Главная книга о паттернах — Design Patterns — увидела свет в 1994-м году, значит, ей уже почти тридцать. По сравнению с ней, Clean Architecture, вышедшая десять лет назад — практически, ребёнок!
Но десять лет для индустрии — гигантский срок. Возможно, эти книги уже устарели и нам они не нужны?
Перечитаем и попробуем разобраться.

Книга [1], известная в народе, как GoF или главная книга о паттернах. Я не нашёл в интернете информации, как познакомились авторы и почему они назвали себя бандой четырёх. Знаю, что все они в то или иное время работали в IBM. Эрих Гамма вместе с Кентом Беком разработал JUnit [2] — предтечу всех современных фреймворков модульного тестирования. Если вы пишите модульные тесты, вы знаете, кого за это благодарить. Джон Влисидис умер в 2005-м в возрасте сорока четырёх лет.
Хелм и Джонсон не участвовали в известных мне проектах.
С момента выхода первого издания прошло 28 лет. Что мы узнали за это время?
1. Термин паттерн трактуется слишком широко. Я слышал про паттерны многозвенное приложение, инверсия зависимостей и микросервис-заместитель (proxy microservice). Ничто из этого я бы к паттернам не отнёс. Автор оригинальной концепции Кристофер Александр [3] говорит не просто о паттернах, а об языке паттернов. Язык предполагает общение и понимание, поэтому я против того, чтобы паттернами называли что угодно. Многозвенная архитектура — это модель, а инверсия зависимостей — принцип. Авторы книги дали паттернам узкое определение, ограничившись объектно-ориентированными языками. Они сформулировали ясную нижнюю границу — паттерном не может быть то, что входит в стандартную библиотеку. Нет паттернов массив и поток.
2. Шестнадцать из двадцати трёх оригинальных паттернов могут быть [4] выражены простыми языковыми средствами, что и продемонстрировал Петер Норвиг в своей статье. Языком программирования был не C++, а LISP. Получается, что некоторые паттерны — это просто досадная необходимость, вызванная слишком низким уровнем языка. Впрочем, в функциональных языках могут быть свои паттерны [5]. Кроме того, паттерн может быть всё-таки языковым средством, который говорит о назначении объекта. Видя в коде Хранилище пользователей (User Repository) мы понимаем, что пользователи хранятся в базе, но нам, слава богу, не надо ничего знать об этой базе. Мы концентрируемся на бизнес-логике.
3. Паттерн Одиночка (Singleton) на поверку оказался антипаттерном. Он нарушает принцип единственной ответственности, потому что экземпляр одиночки выполняет свою основную функцию и одновременно управляет временем своей жизни. Избавиться от одиночки можно, передав управление временем жизни в IoC-контейнер. Термин антипаттерн прочно вошёл в наш лексикон вместе с паттерном.
4. Язык паттернов оказался полезным. Я ни разу в жизни не использовал Интерпретатор (Interpreter) и трепещу перед Посетителем (Visitor), считая его сложным. Я с удовольствием использую Единицу работы (Unit of Work), Хранилище (Repository), Корень композиции (Composite Root), Внедрение через свойство (Property Injection) и десятки других паттернов.
На определённом этапе профессионального становления книгу надо обязательно прочитать. Названия паттернов надо использовать в своей речи и в своём коде. Важно не путать паттерны друг с другом, потому что это и правда язык. Неправильный язык ведёт к заблуждениям.
Обращайте внимание на источники. Если ваш друг Жора придумал паттерн, это не паттерн. Настоящий паттерн, как элемент общего языка, должен быть описан в популярной книге или в статье на посещаемом ресурсе. Паттерн должен быть описан в классической форме, то есть как в GoF, с разделами Название, Назначение, Реализация, Плюсы и минусы.
Словосочетание паттерн микросервис в какой-то статье из интернета не делает микросервис паттерном.

Основной автор книги [6] — Мартин Фаулер. У него пять соавторов, про которых я ничего не слышал. Google подсказывает, что у одного из них есть забытый профиль на GitHub, другой последние десять лет работает управленцем, а третий что-то там из Oracle.
Не смотря на таинственность авторов, книгу следует признать самой полезной книгой о паттернах. У неё есть сокращённое название P of EAA, то есть Patterns of Enterprise Application Architecture. Если GoF вводит нас в тему, то P of EAA погружает нас в самую пучину.
Рассмотрим на примере. В Entity Framework [7] доступ к данным осуществляется через класс DbContext [8].
В DbContext изменения в базу вносятся не тогда, когда вы обновляете поля сущностей или вызываете методы удаления, а когда вызываете SaveChanges. Объект накапливает сделанные вами изменения и затем разом их вносит. Вам не приходится следить за тем, чтобы записи добавлялись или удалялись в правильном порядке — DbContext делает это за вас. Все ваши составные операции атомарны — если одна из них завершится ошибкой, будут отвергнуты все накопленные изменения.
На уровне базы данных мы называем такие операции транзакциями, но транзакции слишком детальны для слоя бизнес-логики — мы можем игнорировать всё, что относиться к уровням изоляции или к вложенности. Паттерн Единица работы (Unit of Work) нужен как раз для этого и DbContext его реализует.
Почему в DbContext нет групповой операции удаления? Потому что в Unit of Work нет такой операции. Как обычно, паттерн хорош в одних сценариях, и не очень хорош в других. Полезно знать его ограничения, чтобы делать свою программу эффективной.
Entity Framework умеет преобразовывать деревья выражений [9] в SQL-запросы. Дерево выражений в данном случае — это Объект-запрос (Query Object), паттерн, описанный Фаулером.
После загрузки данных, Entity Framework заполняет поля объектов из базы. Задача эта нетривиальная [10]. Фаулер описывает набор классов, необходимых для заполнения полей, как паттерн Преобразователь данных (Data Mapper). Наряду с преобразователям, в программах регулярно встречаются Активная запись (Active Record), Шлюз к данным записи (Row Data Gateway), Шлюз к данным таблицы (Table Data Gateway) и другие. Их нет в Entity Framework, но вы найдёте их в ADO.NET [11] или zend-db [12].
Эту книгу надо прочитать, чтобы понимать, что встречается в современной бизнес-разработке. Не обязательно уметь готовить все эти паттерны, достаточно иметь представление о разнообразии решений.

Инверсия зависимостей (Dependency Inversion) — не самая простая концепция. Её трудно объяснить на пальцах, и если вы поняли её на пальцах, возможно, вы поняли её неправильно.
Инверсия зависимостей — старая идея, которую можно встретить даже в стандартной библиотеке C, а именно в функциях bsearch [13] и qsort [14]. Наверняка, это не самый ранний пример, но языку C в 2022-м году исполнилось 53 года, и это значит, что DI, как принцип, известен в мире программирования уже очень давно.
Чтобы принцип работал, нужно придумать, как внедрять зависимости. В C это делается через указатель на функцию с известной сигнатурой, в объектно-ориентированных языках — через параметры конструктора, свойства класса или параметры методов. Паттерны Constructor Injection, Property Injection и Method Injection как раз об этом.
Помимо общих принципов важны, конечно, и детали. Симан показывает [15], как внедрять зависимости вручную, как внедрять их через Castle, Autofac, Unity. Этого достаточно, чтобы перестать беспокоиться о конкретных библиотеках. Они похожи.
Локатор служб (Service Locator) — это антипаттерн, а не паттерн. Книгу надо прочитать, если вы пишете на C#. Если вы пишете на другом объектно-ориентированном языке, она тоже может быть полезна.

Как писать модульные тесты (unit tests)? Есть несколько рекомендаций, которые, к сожалению, трудно применять на практике. Когда я начинал осваивать автоматическое тестирование, я по два часа зависал на тестах. Мне казалось, что для тестирования вот этой вот штуки мне потребуется другая штука, которую тоже нужно написать.
С тестами не задавалось. После того, как они были написаны, их приходилось постоянно переписывать. Да, я знаю, что тесты должны быть устойчивы. Спасибо.
Оказалось, что модульное тестирование, если его готовить правильно, требует серьёзного изучения. На помощь, конечно, приходит книга, и в случае юнит-тестов это — книга Роя Ошероува [16]. Причём здесь архитектура, спросите вы? Архитектура притом, что при написании тестов вам придётся делать классы маленькими и слабо сцепленными.
Книга полезна и для того, чтобы практически освоить искусство написания модульных тестов, и для того, чтобы практически освоить принципы SOLID.

Последняя в списке, но не по значению.
Роберт Мартин, он же дядюшка Боб, известен нам с давних пор. Был такой журнал — C++ Report, который выходил c 1989 по 2002 год. Время рассвета C++. Именно там появились первые современные практики проектирования — до того, как придумали Java.
Дядюшка Боб был редактором этого журнала. Он сформулировал принципы SOLID [17]. Он участвовал в обсуждении Манифеста гибкой разработки [18].
Он написал книгу о чистой архитектуре [19]. Главная идея книги — кто управляет зависимостями, тот управляет миром. Чтобы проект оставался подконтрольным, снижайте количество зависимостей. Организуйте зависимости, без которых нельзя.
Важное знание, которое я получил из книги — никто не знает будущего. Никто не знает, что потребуется от программы через два года. И никакие ваши усилия не позволят вам придумать архитектуру, способную поддержать изменения, которые потребуются через два года. Каким бы умным вы ни были, вы ошибётесь.
Книгу стоит перечитывать время от времени. С каждым новым проектом ваш опыт растёт, и этот опыт надо реструктурировать с помощью специальной литературы. Дело не в том, чтобы узнать новое. Дело в том, чтобы устаканить то, что уже есть в голове.
И какой вывод? Устарели книги? Удивительно, но нет. В нашей индустрии тридцать лет — это почти бесконечность, но, кажется, авторам удалось создать что-то фундаментальное. Не будем убирать эти книги на дальнюю полку. Время ещё не пришло.
Автор: Марк Шевченко
Источник [20]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/376027
Ссылки в тексте:
[1] Книга: https://www.piter.com/collection/uchebniki-dlya-tehnikumov-i-vuzov-14/product/priemy-obektno-orientirovannogo-proektirovaniya-2
[2] JUnit: https://junit.org/junit5/
[3] Кристофер Александр: https://www.patternlanguage.com/
[4] могут быть: http://www.norvig.com/design-patterns/design-patterns.pdf
[5] могут быть свои паттерны: https://youtu.be/o2G22AIhplU
[6] книги: http://www.williamspublishing.com/Books/5-8459-0579-6.html
[7] Entity Framework: https://docs.microsoft.com/ru-ru/ef/
[8] DbContext: https://docs.microsoft.com/en-us/dotnet/api/system.data.entity.dbcontext?view=entity-framework-6.2.0
[9] деревья выражений: https://docs.microsoft.com/ru-ru/dotnet/csharp/programming-guide/concepts/expression-trees/
[10] нетривиальная: https://docs.microsoft.com/ru-ru/ef/core/modeling/relational/
[11] ADO.NET: https://docs.microsoft.com/ru-ru/dotnet/api/system.data.datatable?view=netframework-4.8
[12] zend-db: https://docs.zendframework.com/zend-db/row-gateway/
[13] bsearch: https://ru.cppreference.com/w/cpp/algorithm/bsearch
[14] qsort: https://ru.cppreference.com/w/cpp/algorithm/qsort
[15] показывает: https://habr.com/ru/company/piter/blog/175411/
[16] книга Роя Ошероува: https://dmkpress.com/catalog/computer/programming/dot_net/978-5-94074-945-6/
[17] SOLID: http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
[18] Манифеста гибкой разработки: https://agilemanifesto.org/iso/ru/manifesto.html
[19] книгу о чистой архитектуре: https://www.piter.com/collection/all/product/chistaya-arhitektura-iskusstvo-razrabotki-programmnogo-obespecheniya
[20] Источник: https://habr.com/ru/post/671580/?utm_source=habrahabr&utm_medium=rss&utm_campaign=671580
Нажмите здесь для печати.