Главная » Хабрахабр » [Из песочницы] Рефакторинга много не бывает

[Из песочницы] Рефакторинга много не бывает

Привет, Хабр! Представляю вашему вниманию перевод статьи "Refactoring — oops, I’ve been doing it backwards" автора Джастина Фуллера (Justin Fuller).

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

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

Так почему рефакторинг не оправдал моих надежд?

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

Думаю, что да. В конце концов, все получилось, но был ли мой код более сложным, чем нужно?

Главенство принципов над целью

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

Думаю, что да. Является ли это рецептом хорошего кода? Проблема возникает, когда я ставлю принципы превыше цели. Проблема возникает тогда, когда мой код сосредоточен на том, чтобы соответствовать SOLID-принципам, а не выполнять то, для чего он был создан.

Это напомнило мне мантру: «Заставь это работать, сделай это правильно, сделай это быстро». Помните мое размышление в самом начале? Я делал все правильно, быстро, а потом делал так, чтобы это работало! Я понял, что не следовал этому порядку.

Заставьте это работать

Когда я начал писать больше статей на Medium, стало ясно, что хороший стиль письма не приходит вот так сразу. Сначала я должен изложить все свои мысли на странице. Я должен увидеть, куда эти мысли меня приведут. Затем я должен превратить их в некую полусвязную и неразборчивую версию того, что только что выплеснулось.

То же самое может случиться и с кодом.

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

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

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

Имейте причину для рефакторинга

Наличие функционального или чистого кода также не является причиной. Наличие SOLID-кода не является причиной.

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

Для того, чтобы бизнес-логика могла использоваться несколькими реализациями. Почему мы инвертируем зависимости?

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

«Хороший код — расширяемый код» — это тоже не причина. «Считается, что лучше всего использовать инверсию зависимостей» — это не причина. Мне все еще нужна инверсия зависимостей? Что если у меня есть только пара неизменяемых зависимостей? Что если ничего не нужно для расширения моего кода, и я вообще не планирую этого делать? Пока что нет. Нет! Должен ли я усложнять мой код, чтобы просто отметить этот флажок?

Посмотрите на следующий пример.

// not extensible
function getUser()
}
// Extensible class User { constructor(options = {}) { this.userData = options } get() { return this.userData } set(key, value) { this.userData[key] = value }
} // not extensible function getUser() { return { name: 'Justin', email: 'justinfuller@email.com' }
}
// Extensible class User { constructor(options = {}) { this.userData = options } get() { return this.userData } set(key, value) { this.userData[key] = value }
}

Что вы предпочитаете? Что обычно пишете в первую очередь? Конечно, класс User гораздо более расширяем, потому что он может обрабатывать не только имя и электронную почту. Он также может быть расширен дочерним классом, может быть SuperUser, который будет иметь гораздо больше методов, но все еще использует классические методы get() и set().

Тем не менее класс User может быть излишним, и ваш код может стать гораздо сложнее, чем это когда-нибудь может понадобиться.

Мой совет — придерживайтесь простейшего шаблона.

Порядок сложности

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

Этот список держит меня под контролем и предохраняет от преждевременного рефакторинга. Обычно я помещаю что-то в объект вместо простой константной переменной.

Баланс

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

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

Наш код не может быть обслуживаемым, если он не будет правильно работать. Мы должны быть бдительными и следить за тем, чтобы обе потребности оставались на своем месте. С другой стороны, трудно заставить плохой код работать правильно.

В следующий раз, когда захотите провести рефакторинг своего кода, подумайте, а стоит ли?


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Ускоряем неускоряемое или знакомимся с SIMD

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

Kонсенсус в Exonum: как он работает

ExonumTM — это наш открытый фреймворк для создания приватных блокчейнов. Сегодня мы расскажем, как работает его алгоритм консенсуса. Изображение: Bitfury Зачем нужны алгоритмы консенсуса Прежде чем перейти к рассказу о том, как устроен алгоритм консенсуса ExonumTM, поговорим о том, зачем ...