Основные возможности платформы Hexlet не доступны в вашем браузере.
Пожалуйста, обновитесь. Выбрать браузер.
Разработка

Неправильная абстракция

Это перевод статьи Sandi Metz The Wrong Abstraction.

Я размышляю о последствиях «неправильной абстракции». Мой доклад с RailsConf 2014 «all the little things» включал раздел, в котором я высказала такое мнение:

дублирование значительно дешевле, чем неправильная абстракция

А заключение я подытожила советом:

выбирайте дублирование вместо неправильной абстракции

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

This, a million times this! “@BonzoESC: “Duplication is far cheaper than the wrong abstraction” https://twitter.com/pims/status/442010383725760512

Сила эмоций заставила меня понять, насколько широко распространена и неразрешима проблема «неправильной абстракции». Я начала задавать вопросы и пришла к следующему шаблону:

  1. Программист A натыкается на дублирование.

  2. Программист A извлекает дублирование и как-то его называет.

    Это создает новую абстракцию. Что-то вроде нового метода или, возможно, даже нового класса.

  3. Программист А заменяет дублирование новой абстракцией.

    Ах, код совершенен. Программист А довольно сваливает.

  4. Проходит время.

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

  6. Программисту B поручают реализовать это требование.

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

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

  7. Появляется ещё одно новое требование.

    Программист X. Ещё один дополнительный параметр. Ещё одна условная конструкция. Зацикливание до момента, пока код не станет слишком сложным для понимания.

  8. Тут в истории появляетесь вы, и ваша жизнь резко делает драматический поворот в худшую сторону.

Существующий код оказывает сильное влияние. Само его наличие доказывает, что он правильный и необходимый. Мы знаем, что код — это приложенные усилия, и сильно мотивированы сохранить ценность этих усилий. К сожалению, печальная правда в том, что чем сложнее и непостижимей код, т. е. чем массивней вклад в его создание, тем больше мы чувствуем давление сохранять его («ложные выводы о необратимых затратах»). Как будто наше бессознательное говорит нам: «Боже, тут всё так запутано — наверно, потребовалась вечность, чтобы сделать всё как надо. Конечно, это действительно очень, очень важно. Грех, если все эти усилия пропадут впустую.»

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

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

  1. Восстановите дублирование, вернув абстрагированный код туда, где происходит вызов.
  2. Внутри каждого вызова, используйте передаваемые параметры, чтобы определить подмножество объединённого кода, который этот конкретный вызов исполняет.
  3. Удалите части, которые не нужны для данного вызова.

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

Мне встречались случаи, когда люди пытались уверенно продолжать работать с неправильной абстракцией, но у них это слабо получалось. Добавление новых фич было невероятно сложным, а каждый положительный результат ещё больше усложнял код, что делало добавление следующей фичи ещё сложнее. Когда они поменяли свою точку зрения с «мне нужно сохранить своё вложение в этот код» на «этот код имел смысл какое-то время, но, мы, возможно, извлекли из него максимум пользы», и позволили себе переосмыслить свои абстракции, с точки зрения текущих требований, всё упростилось. После того, как они объединили код, стало очевидно куда двигаться дальше, а добавление новых фич стало быстрее и проще.

Мораль этой истории? Не попадайте в ловушку ложных выводов о необратимых затратах. Если вы заметите, что передаёте параметры и добавляете условные пути через общий код, абстракция неверна. Возможно, было правильно с этого начинать, но тот день прошёл. Как только ошибочность абстракции доказана, лучшая стратегия — восстановить дублирование и оно покажет, что правильно. Хоть иногда и имеет смысл сохранить несколько условных конструкций, чтобы получить представление о том, что происходит, чем раньше вы откажетесь от неправильной абстракции, тем меньше вы пострадаете.

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

Поделиться Вконтакте
Отправить в Телеграм