img

Анти-шаблоны – их следует избегать

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

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

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

Что же такое анти-шаблон?

Анти-шаблон – это термин, который описывает, как НЕ НАДО решать периодически возникающие в вашем коде проблемы. Анти-шаблоны - это не самый лучший вариант архитектуры программного обеспечения, и, как правило, это неэффективные и мало кому понятные исправления. 

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

В этой статье мы обсудим шесть анти-шаблонов: Spaghetti CodeGolden HammerBoat AnchorDead CodeProliferation of Code и God Object.

Спагетти-код (Spaghetti Code)

Спагетти-код – это самый известный анти-шаблон. Это код, в котором структура просто отсутствует. 

Никакой модульной организации. Случайные файлы разбросаны по случайным каталогам. Трудно уследить за всем потоком, поскольку все запутано капитально (прям как спагетти).

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

 Что здесь происходит? Я не могу ничего понять

image.png

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

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

Золотой молоток (Golden Hammer)

«Тот, у кого из всех орудий есть только молоток, склонен на любую проблему смотреть, как на гвоздь», - Аврахам Маслоу.

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

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

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

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

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

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

Лодочный якорь (Boat Anchor)

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

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

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

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

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

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

Мертвый код

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

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

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

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

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

Он начал комментировать и «отключать» какие-то фрагменты кода, чтобы посмотреть, что на самом деле «раздувает» кодовую базу. Он назвал такой подход Monkey Testing. Осторожно! Это может быть слишком рискованно!

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

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

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

Ненужное усложнение кода (Proliferation of Code)

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

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

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

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

Всемогущий объект

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

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

Иногда его называют анти-шаблоном Swiss Army Knife (Швейцарский армейский нож). Дело в том, что такой нож предназначен для того, что резать веревку, но при этом его также можно использовать в качестве пилки для ногтей, пилы, пинцета, ножниц, открывашки для бутылок и штопора. 

В таком случае вам лучше разделить код на модули. 

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

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

Буква S в этой аббревиатуре обозначает «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;
}   

Если вы разделите этот интерфейс на несколько, то ваш код сможет четко понимать, какой интерфейс за что отвечает, и ему не нужно будет заставлять классы реализовывать всё (engine, customerId, model и т.д.), несмотря на то, что ему нужно только wingspan.

Заключение

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

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

Ссылка
скопирована
Программирование
Скидка 25%
Python-программист с нуля
Стань разработчиком на одном из самых популярных языков программирования.
Получи бесплатный
вводный урок!
Пожалуйста, укажите корректный e-mail
отправили вводный урок на твой e-mail!
Получи все материалы в telegram и ускорь обучение!
img
Еще по теме:
img
За последние годы микрослужбы прошли путь от обычного переоцененного модного словечка до вещи, которую вы, как специалист по про
img
Введение Резидентные базы данных (или хранилища в памяти) по большей части делают упор на хранилище данных в памяти, а не на жес
img
  Многие люди рассуждают так: «зачем, ну зачем мне изучать еще один язык программирования?» Для них это лишнее, и они стараютс
img
Введение Объекты в Kubernetes – это базовые постоянные сущности, которые описывают состояние кластера Kubernetes. Модули – это э
img
  Довольно часто мы встречаемся с компонентами архитектуры программного обеспечения, которые являются частью любой системы, но п
img
  Ключевые отличия между JDK, JRE и JVM: JDK – это набор средств для разработки программного обеспечения, JRE – это програ
Комментарии
ЛЕТНИЕ СКИДКИ
40%
50%
60%
До конца акции: 30 дней 24 : 59 : 59