Многие разработчики считают принципы программирования обязательными и используют их по дефолту во всех проектах. На самом деле большинство из них нереализуемы на практике — докажем это на нескольких примерах.

Эта статья — адаптированная расшифровка доклада о принципах программирования, прочитанного CEO Хекслета Кириллом Мокевниным на конференции Trampoline Meetup летом 2021 года.

Если попытаться вспомнить самые распространенные принципы программирования и подходы к разработке, то в списке наверняка окажутся CodeStyle, SOLID, DRY, KISS, YAGNI, CQS, LoD. И это далеко не исчерпывающий перечень.

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

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

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

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

Рассмотрим несколько примеров.

DRY

Принцип Don’t Repeat Yourself (Не повторяйтесь) может хорошо работать в коде, но в тестах его применять нет никакого смысла. Если убрать дублирование кода, остаются только сложные абстракции. Если в тестах появляются сложные абстракции, они сами превращаются в код, который сложно понимать и нужно тестировать.

SOLID

Аббревиатура, предложенная Робертом Мартином. Разберем несколько букв из нее:

  • S или Single-responsibility principle (Принцип единственной ответственности) применяется для проектирования объектов, классов и методов. Его суть в том, что каждый из перечисленных элементов должен отвечать только за что-то одно.

    Это очень абстрактный принцип, который сформулирован в духе «хорошо делай — хорошо будет». Несколько раз я проводил эксперимент: просил двух разработчиков ответить, реализован ли принцип в отдельно взятом классе. Оба всегда отвечают нет и называют совершенно разные причины.

    React-приложения часто состоят только из одного класса. Это прямо противоречит принципу, но на практике не означает, что приложение реализовано неправильно.

  • L или Liskov substitution principle (Принцип подстановки Лисков) в интерпретации Мартина касается классов, но Барбара Лисков в оригинале сформулировала его для типов. На самом деле суть принципа в наследовании интерфейсов, чего на практике почти никто не делает — это происходит крайне редко и только в библиотеках.

Как и S, L — важный и правильный, но бесполезный на практике принцип.

LoD

Law of Demeter или закон Деметры говорит нам примерно следующее: «не разговаривай с незнакомцами и не создавайте ненужных зависимостей». Если рассматривать с точки зрения этого принципа код u.getСompany.getOwner().get …, то он будет написан неверно: в нем есть зависимости, которые могут привести к проблемам. В данном случае принципу можно последовать и переписать код, а можно оставить все как есть. Закон Деметры — это, скорее, рекомендация, о которой стоит помнить.

CQS

CQS или сommand-query separation (Разделение команд и запросов) — принцип разделения запросов (query) и команд на обработку (например сохранение данных или удаление), каждый из которых либо читает данные, либо обновляет их. Метод может быть запросом, возвращающим какое-то значение, или командой, но не одновременно тем и другим.

Например, если на Rails вы видите, что есть user.valid?, но нет if, в большинстве случаев такая ситуация связана с колбеком. Это могут быть данные, получаемые из формы, которые нужно рассортировать по категориям. valid отвечает за то, чтобы заполнить некую логику после валидации. Это ненормально и здесь помогает принцип CQS. В данном случае предикат меняет внутреннее состояние, но он не должен этого делать.

На одном из собеседований в Хекслет Rails-программист получила задачу написать генератор форм. В условии были классы с объектами для каждого метода и сам метод to string. Программистка реализовала генератор, использовав внутри to_string() опцию options.delete(:class).

Если использовать объект второй раз, он отдаст другой вывод, поскольку delete — мутирующий метод. Это недопустимое программирование, и эта опция была у программистки во всех методах.

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

Заключение

Мартин Фаулер в одной из лучших книг по прикладному программированию «Шаблоны корпоративных приложений» описывает принцип распределения объектов так: «не распределяйте объекты». С практической точки зрения это очень понятно и вполне применимо.

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