image Как дела, Хаброжители?

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

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

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

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

Для кого написана эта книга
Книга написана для программистов с практическим опытом от 2 до 5 лет. Предполагается, что вы уже знаете хотя бы один язык программирования. Также желательно, чтобы вы построили хотя бы одну достаточно крупную систему, чтобы представлять, с какими проблемами разработчики сталкиваются при масштабировании. Примеры написаны в стиле JavaScript, направленном на читаемость кода. Если вы понимаете код C, C#, C++ или Java, у вас не будет особых сложностей.

Отдел маркетинга все еще должен согласовывать действия с разработчиками


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

image

Подобных запросов очень много, и они продолжают поступать. Все запросы очень похожи — даже код их реализации был похожим. Разве абстрактный барьер не должен был это предотвратить? До этого отдел маркетинга мог просто обратиться к структуре данных. Теперь он снова вынужден ждать команду разработки. Очевидно, что абстрактный барьер не работает.

Признак «кода с душком»: неявный аргумент в имени функции


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

image

В этом коде присутствует серьезный признак «кода с душком». Впрочем, если честно, от этих строк вообще разит. Первый и самый заметный признак — дублирование кода. Эти четыре функции почти идентичны.

Но есть и другой, более тонкий признак: главное различие между функциями — строки, определяющие поле, — также содержится в имени функции. Все выглядит так, словно имя функции (или его часть) передается в аргументе. Вот почему этот признак называется неявным аргументом в имени функции. Вместо явной передачи в аргументе значение «передается» как часть имени.

image

image

Директор по маркетингу: Код может пахнуть?

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

Ким: Да! Этот код определенно попахивает. Только посмотри на все это дублирование.

Дженна: Да, код действительно очень похож. Но я не вижу, как избавиться от дубликатов. Нам нужны средства для назначения цены и количества единиц товара. Разве это не разные функции?

Ким: Дублирование означает, что эти функции почти одинаковы. Единственное различие — строка с именем поля ('price', 'quantity' или 'tax').

Дженна: Да, понимаю! И эта строка также присутствует в имени функции.
Ким: Точно. И это признак «кода с душком»: вместо передачи в аргументе имя поля становится частью имени функции.

Директор по маркетингу: И вы говорите, что его можно исправить?

Ким: Да. Я знаю прием рефакторинга, который позволит заменить все четыре функции одной. Для этого нужно сделать имя поля первоклассным значением.

Директор по маркетингу: Первоклассным? В смысле первого класса — как в поезде или самолете?

Ким: Хм. Да, пожалуй. Это просто означает, что имя поля становится аргументом. Мы определим его позднее.

Рефакторинг: явное выражение неявного аргумента


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

Последовательность действий выглядит так:
1. Выявление неявного аргумента в имени функции.
2. Добавление явного аргумента.
3. Использование нового аргумента в теле вместо жестко фиксированного значения.
4. Обновление кода вызова.

Посмотрим, как провести рефакторинг функции setPriceByName(), которая может задать только цену, в функцию setFieldByName(), способную задать значение любого поля товара.

image

Применение этого рефакторинга к коду позволяет заменить четыре существующие функции одной обобщенной, — и кто знает, сколько еще функций нам не придется писать благодаря обобщенной функции setFieldByName().

Что же здесь произошло? Имя поля было преобразовано в первоклассное значение. Ранее имя поля не раскрывалось перед клиентами API, кроме как в случае неявного раскрытия как части имен функций. Теперь оно стало значением (в данном случае строкой), которое может передаваться в аргументе, но также может храниться в переменной или массиве. Именно это имеется в виду под первоклассным значением: для работы с ним может использоваться полный набор языковых средств. Преобразование к первоклассному статусу является темой этой главы.

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

Директор по маркетингу: И нам не придется подавать заявку на изменение каждого поля?

Дженна: Вот именно. Теперь вы можете обратиться к любому нужному полю — просто укажите его имя в строковом формате и передайте его при вызове.

Директор по маркетингу: Как мы узнаем, как называется то или иное поле?

Ким: Очень просто. Мы сделаем имена частью спецификации API. Они будут частью абстрактного барьера.

Директор по маркетингу: Хмм… Идея мне начинает нравиться. Но тогда другой вопрос: а если вы добавите новое поле в спецификацию корзины или товара? Что тогда?

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

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

Ким: Так и должно быть! В старом варианте вы должны были знать набор функций (и подавать запросы на новые функции!), а теперь достаточно знать одну функцию и набор имен полей.

image

Более подробно с книгой можно ознакомиться на сайте издательства:
» Оглавление
» Отрывок

По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Для Хаброжителей скидка 25% по купону — Мышление

Комментарии (9)


  1. wolfy_str
    00.00.0000 00:00
    +6

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

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


    1. ksbes
      00.00.0000 00:00
      +1

      Самое смешное, что часто так в жизни и бывает. Не раз видел. Только там ещё и этот самый джун пытается доказать что старый код — это именно то как надо писать.


      1. wolfy_str
        00.00.0000 00:00
        +1

        А мне вообще нравится такой подход, разбирать каждую мелочь. Многие скажут слишком много воды, но когда что то непонятно хочется чтобы разобрали тему как можно подробнее. Я заметил, что в основном в переводной английской литературе это почти всегда так. Всё разжёвывают. Да, воды много зато максмимально понятно, главное терпение всё прочитать.

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

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


    1. GospodinKolhoznik
      00.00.0000 00:00

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


    1. tarielr
      00.00.0000 00:00

      Хмм... А вы сейчас точно про энтерпрайз? Просто у нас в банке на такую задачу пару митингов с владельцем продукта, аджай покер с командой и занесение в бэклог. И не факт, что в ближайший спринт попадет. )))


  1. AstarothAst
    00.00.0000 00:00

    .


  1. malferov
    00.00.0000 00:00
    +1

    Раньше клиенту надо было знать четыре функции, а после рефакторинга надо знать столько же полей (внутренней реализации) + название одной функции + оказалось, что с использованием строковых аргументов ещё и безопасность может быть нарушена, о чем в книге расскажут позже.

    Мне кажется, с такими примерами надо быть поосторожнее. Одно дело сделать обобщенную функцию внутри, другое дело открывать такой метод наружу. Нет, когда-нибудь и где-нибудь вполне можно. Просто осторожно.

    Иначе говоря, — не надо начинать срочно рефакторить код, создавая обобщенные методы.


  1. ngekht
    00.00.0000 00:00

    Раз уж отсылаются к мастеру, то прям просится другая цитата оттуда:

    “А сегодня я нашел два способа завязывать шнурки, один хорошо чтобы ходить, другой - что бы падать”

    Старый код, конечно, попахивал, после рефакторинга - завонял. Раньше логика зависимая от строковой константы была хотя бы стыдливо спрятана в API, а теперь торчит наружу, поджидая неосторожного джуна или что еще хуже - клиента, чтобы тот выстрелил себе в ногу. И главное что статическим анализом кода такое уже не поймать, только дебаг, только хардкор, причем обязательно с заходом в функцию, чтобы понять чеж там происходит. “SOLID? Never heard about…”

    Больше, понимаешь, книжек, хороших и разных.


  1. grigorym
    00.00.0000 00:00

    Я всё понимаю, абстракции-шмабстракции. Но нормально работать с кодом, в котором вместо всех object.setPrice(x) написано object.set_prop('price', x), можно будет только когда синтаксический анализатор будет такое считать эквивалентным и находить, а пока - нет. Хуже бывает только ещё object.set_prop(to_lower('PRICE'), x)