Антипаттерны

      Комментарии к записи Антипаттерны отключены

Помечено: , , ,

В этой теме 0 ответов, 1 участник, последнее обновление  Васильев Владимир Сергеевич 2 мес., 2 нед. назад.

  • Автор
    Сообщения
  • #3626

    Если «паттерны» — это шаблоны решения каких-либо задач, то «антипаттерны» — то шаблоны ошибок в решении этих задач.

    Copy and paste

    Программисту необходимо написать две похожие функции. Он пишет первую функцию, потом «Копировать» — «Вставить», и вносит необходимые изменения. Знакомая ситуация?

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

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

    Spaghetti

    Вам когда-нибудь на работе доставалась в наследство функция размером пять тысяч строк? Если да, то вы были очевидцем живого примера антипаттерна «Спагетти». Этот антипаттерн имеет место быть у программистов, для которых существует всего один критерий качества программы — «Программа работает».

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

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

    Golden hammer

    В переводе с английского, «Golden hammer» означает «Золотой молот». «Golden hammer» это применение какого-то конкретного паттерна для решения всех возможных и невозможных задач. Программист «влюбляется» в какой-то паттерн, и начинает использовать его где только это возможно, как универсальное решение всех проблем, как золотой молот.
    Помимо профессиональных программистов, золотой молот также встречается среди новичков. Однако, в этом случае его причины иные — новичку просто лень изучать что-то новое, и он пытается решать все задачи единственным методом, который он освоил.
    Очевидно, что подобный подход совершенно не рационален — сколько задач, столько и решений.

    Hard code

    Данный антипаттерн подразумевает жесткое «вшивание» в программный код различных данных, касающихся окружения программы. Например, это могут быть: путь к файлу конфигурации, имя почтового сервера, имя bluetooth-устройства, имя процесса, с которым взаимодействует программа, и так далее. Этот список можно продолжать долго. Одни словом — «захардкодить» означает прописать значение каких-либо данных непосредственно в коде, вместо того, чтобы передавать их в качестве параметра.

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

    Случай из жизни:

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

    Magic numbers

    «Magic numbers» это «хардкодинг» каких-либо числовых значений. Например, это могут быть математические и физические константы, какие-либо коэффициенты или поправочные параметры. Вот пример этого антипаттерна:

    std::size_t packet_size = 74 * 12 * sizeof(packet) + 65;
    
    float length = 2.0f * 3.1415926f * circle.get_radius() - 15.4f;

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

    Magic strings

    Этот антипаттерн, как можно догадаться из названия, похож на «Magic numbers». «Magic strings» — это прописывание в коде строковых значений для сравнения с какими-либо параметрами. Например, для сравнения со значением названия события, как в следующем примере:

    if(event.type() == "begin")
    {
        // Какой-то код
    }
    else if(event.type() == "process")
    {
        // Какой-то код
    }
    else if(event.type() == "commit")
    {
        // Какой-то код
    }

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

    Reinventing the wheel

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

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

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

    Cargo cult programming

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

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

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

    Blob

    «Blob» — это класс, на который возложено слишком много обязанностей. «Предпочитайте минимальные классы монолитным» — так звучит рекомендация номер 33 из книги Саттера и Александреску «Стандарты программирования на C++». Каждый класс должен иметь одно конкретное назначение, которое можно описать несколькими словами. Большие классы тяжело поддерживать, они очень неповоротливы и не любят рефакторинг. Лучше иметь пять маленьких классов, решающих конкретные задачи, чем один большой и универсальный класс.

    Старая индейская мудрость гласит — «не складывайте все яйца в одну корзину».
    Случай из жизни:

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

    Programming by permutation

    Анекдот:

    Звонок пользователя в службу техподдержки.
    — Алло, это служба техподдержки?
    — Да.
    — У меня проблема, мой модем не хочет работать!
    — Давайте пропишем вам строчку инициализации.
    — Давайте.
    — ats0=1
    — Спасибо!
    Через пять минут.
    — У меня все еще не работает!
    — Да? Ну давайте запишите другую.
    — Я слушаю.
    — at+ms:11,1,2400,33600
    — Спасибо!
    Проходит еще пять минут.
    — У меня модем все еще плохо работает!
    — Ну что, пишите еще одну строчку…
    — Слушаю.
    — at*S9=40S10=100i14
    — Спасибо.
    Прошло еще пять минут.
    — Алло!!! У меня сгорел модем!!!
    — Жаль, а то у меня еще столько в запасе строчек инициализации осталось…

    Programming by permutation — это программирование методом подбора. Подбора параметров, токенов, порядка и наличия вызовов функций, и так далее. «Попробуем так… Не работает… А есть так… Вот, так вроде бы ничего».

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

    Interface bloat

    «Interface bloat» — так называется слишком сильно «раздутый» интерфейс, то есть определяющий слишком много функций, имплементация которых превращается в отдельную проблему. В некотором смысле можно сказать что «Interface bloat» — это частный случай антипаттерна «Blob».
    Обычно «Interface bloat» появляется в тех интерфейсах, которые не были тщательно продуманы на этапе проектирования. Вместо того, чтобы пересмотреть архитектуру, программисты просто добавляют новые интерфейсные функции по мере возникновения в них необходимости. Особенно часто этот антипаттерн встречается в коде, у которого несколько раз менялись хозяева.
    Подсистема с переусложненным интерфейсом неповоротлива, неуклюжа, трудна в сопровождении и совершенно не любит рефакторинга. «Interface bloat» лишает код универсальности, делая его одноразовым. Избегайте сложных интерфейсов.

    Lava flow

    На википедии антипаттерн «Lava flow» находится в разделе «Programming anti-patterns», но я считаю, что ему все же место в разделе «Management anti-patterns». По крайней мере как это бывает в России.

    Предположим что у вас есть какая-то подсистема. Эта подсистема досталась вам в наследство от прошлого владельца. Она вроде как работает, но при этом имеет совершенно уродливую реализацию, затрудняющую ее дальнейшее сопровождение и развитие, и является самым настоящим музеем антипаттернов. Для того чтобы привести эту систему в порядок (что чаще всего означает «переписать заново»), придется потратить немало сил и времени. Многие руководители боятся пойти на такой рискованный шаг, продолжая двигаться по пути наименьшего сопротивления. Мол, «работает, и ладно». Подобный отказ от приведения системы в порядок и является антипаттерном «Lava flow».

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

    Предлагаю вашему вниманию мини-пьесу, написанную лично мной, которая так и называется: «Lava flow»:

    Генеральный (в отдел кадров). Уволить этих бездарей! Найдите мне нормальных специалистов!

    …спустя три недели новые специалисты сидят на рабочих местах и изучают проект.

    Junior developer (про себя). Как тут все сложно… Жизни не хватит чтобы понять как все это работает… Меня наверно уволят…

    Senior developer (громко и недовольно). Кто учил этих мудаков программировать?!! Нет, ну вы посмотрите что они тут пишут!

    Главный архитектор (задумчиво). Если бы это была лошадь, я бы посоветовал ее пристрелить…

    Team leader (нервно). Спокойно, мужики. Заказчик хочет чтобы мы просто кое-что тут дописали. Оставим все как есть, просто доделаем то, что требуется.

    Project manager (обращаясь к разработчикам). Проект конечно тяжелый, но мы ведь справимся! Тем более мы будем вести разработку по самой лучшей методологии! Нам поможет XP! Scrum! TDD! (нужное подчеркнуть).

    Главный архитектор (поймав project manager-а в курилке). Ты ведь понимаешь, что проект уже бьется в предсмертных конвульсиях. Его нужно полностью переписать.

    Project manager (Главному архитектору). Да все я понимаю, только кто мне это позволит? Знаешь сколько уже потрачено бабла на этот проект? Попробуй объяснить генеральному, что нужно все переделать, я посмотрю насколько быстро ты отыщешь живописное местечко под названием «нах#й».

    Project manager (Оставшись один в курилке, глядя в окно). Вот начнем мы писать проект заново. И облажаемся. Я же во всем виноват буду, с меня же шкуру спустят. Ну его нафиг. А так, если сроки и завалим, то я всегда смогу свалить все на то, что проект уже был полным говном, когда достался нам.

    …спустя полгода.

    Генеральный (в отдел кадров). Уволить этих бездарей! Найдите мне нормальных специалистов!

    Premature optimization

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

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

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

    Premature pessimization

    Этот антипаттерн называется «преждевременная пессимизация», и является обратной крайностью антипаттерна «Premature optimization».

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

    Зачем люди это делают? Понятия не имею. Может быть пытаются защититься от обвинений в преждевременной оптимизации, может быть хотят впоследствии оптимизировать код, и порадоваться наглядной разнице в производительности (похвастаться перед начальником), может быть что-то еще.

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

    Blind faith

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

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

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

    Exception handling

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

    Исключения должны использоваться с одной единственной целью — проинформировать систему об ошибке. Использование исключений как инструмента для управления логикой программы вносит неоднозначность. Глядя на конструкцию try-throw-catch, программисту совершенно не очевидно, для чего именно эта конструкция используется. Кроме того, управление логикой через исключения и система оповещения об ошибках, построенная на исключениях, могут попросту мешать друг другу, поскольку они построены на одном механизме.

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

Для ответа в этой теме необходимо авторизоваться.