Антипаттерны в коде, которых нужно избегать

0
1130
views

Перевод статьи «Anti-patterns You Should Avoid in Your Code».

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

Но мы все еще можем повстречать в программах антипаттерны. Это особенно касается программ, написанных давно или в спешке.

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

Что же такое антипаттерн?

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

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

Спагетти-код

Спагетти-код — самый известный антипаттерн. Это код, практически не имеющий никакой структуры. В нем нет никаких модулей. Файлы случайным образом разбросаны по случайным папкам. Ход программы трудно проследить, он полностью перепутан (как спагетти).

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

Что оно делает?! Я не могу в этом разобраться

Спагетти-код это не только кошмар в плане поддержки. Помимо этого он также делает практически невозможным добавление нового функционала.

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

Дополнительно почитать о спагетти-коде можно здесь.

Золотой молоток

«Я думаю, что если твоим единственным инструментом является молоток, то на что угодно хочется смотреть как на гвоздь», — Абрахам Харольд Маслоу.

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

Но теперь почему-то всегда все заканчивается использованием этой архитектуры. Винт с плоской головкой? Молоток. Винт с крестообразным шлицем? Молоток. Что? Вам нужен шестигранный ключ? Но ведь у вас же есть молоток!

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

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

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

Не пренебрегайте постоянной учебой на протяжении всей своей карьеры. Для каждого проекта выбирайте наиболее подходящий язык. Продумывайте архитектуру и выходите за рамки привычного. Исследуйте и пробуйте в действии новые инструменты и новые способы решения стоящих перед вами проблем.

О золотом молотке можно почитать здесь.

Лодочный якорь

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

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

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

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

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

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

Об антипаттерне «лодочный якорь» можно почитать здесь.

Мертвый код

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

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

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

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

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

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

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

О мертвом коде можно почитать здесь.

Разрастание кода

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

Антипаттерн «разрастание кода» — это когда в кодовой базе есть объекты, существующие исключительно для вызова других, более важных объектов. То есть это объекты-посредники в чистом виде.

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

Простое решение — просто удалить лишний объект. Переместите ответственность за вызов необходимого объекта на вызывающий объект.

О разрастании кода можно почитать здесь.

Божественный объект

Если в вашей кодовой базе повсеместно требуется доступ к одному объекту, этот объект вполне может быть «божественным».

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

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

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

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

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

Буква S в аббревиатуре SOLID означает Single Responsibility — принцип единственной ответственности. Суть его в том, что каждый класс (модуль, функция) должен отвечать за одну часть системы, а не за несколько.

Как насчет следующего интерфейса?

interface Animal {
        numOfLegs: string;
        weight: number;
        engine: string;
        model: string;
        sound: string;
        claws: boolean;
        wingspan: string;
        customerId: string;
}

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

Как насчет такого изменения?

interface Animal {
        numOfLegs: string;
        weight: number;
        sound: string;
        claws: boolean;
}

interface Car {
        engine: string;
        model: string;
}

interface Bird {
        wingspan: string;
}

interface Transaction {
        customerId: string;
}   

Разделение интерфейсов позволит читателю кода ясно увидеть зону ответственности каждого, а кроме того классам, которым нужен только wingspan, не придется реализовать также engine, customerId и model.

О божественных объектах можно почитать здесь.

Итоги

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

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

ОСТАВЬТЕ ОТВЕТ

Please enter your comment!
Please enter your name here