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

Изначально я опубликовал свой рассказ о своем блоге: https://blog.cerebralab.com/Bimodal_programming_%E2%80%93_why_design_patterns_fail

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

Есть проблема, с которой я всегда сталкивался с идеей «принципа проектирования» и «паттернов проектирования», частично это происходит из личных эмпирических данных, которые не показали мне никого из хороших программистов, которых я знал (то есть парней, которые на самом деле написали 90% критический код) действительно, казалось, использовал их.

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

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

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

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

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

Экскурсия в крупные проекты и рекомендации по кодированию

Мне приходят в голову три больших проекта с открытым исходным кодом: pytorch, llvm и clickhouse.

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

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

Хорошим примером являются рекомендации LLVM: https://llvm.org/docs/CodingStandards.html#introduction

Они больше похожи на мешанину советов, уловок и обычаев. Давайте рассмотрим несколько фрагментов, чтобы проиллюстрировать это:

  • Используйте ранний выход и продолжайте упрощать код
  • Не закрывайте «внутренние» заголовки
  • Больше не использовать после возврата
  • Не используйте метки по умолчанию в полностью закрытых переключателях перечислений
  • По возможности используйте для циклов на основе диапазона
  • Не используйте RTTI или исключения

По сути, рекомендации по кодированию в крупных проектах часто имеют форму:

  • Никогда не используйте X, если вам действительно не нужно использовать X, и в этом случае мы можем его просмотреть, а если нет способа обойти это, давайте использовать X, но только на этот раз.
  • Когда вы можете выбирать между X, Y, Z, выбирайте Z, если нет действительно веских причин для использования X и Y.
  • Использование A не запрещено, но будьте осторожны, потому что, используя A, вы, скорее всего, облажаетесь.
  • Вы, вероятно, обнаружите, что часто используете шаблон X, вот почему он все еще шаблон, а не функция, и вот как правильно его написать.
  • У нас есть абстракции X, Y, Z ... они очень полезны, прочтите внимательно, что они делают, и подумайте об их использовании, когда они могут подойти.
  • Вот как мы тестируем вещи, тестирование важно, и вы тоже должны делать это, если у вас нет причин не делать этого.
  • Вот как мы проверяем материалы. Имейте это в виду, когда вы отправляете материалы на проверку или когда сами проверяете изменения.
  • Это конечные цели нашего продукта, всегда помните о них.

Чтобы найти другие ссылки, ознакомьтесь с рекомендациями по кодированию ядра Linux: https://www.kernel.org/doc/html/v4.10/process/coding-style.html или рекомендациями по кодированию Rustc: https: //rust-lang.github.io/rustc-guide/conventions.html или любой другой произвольный большой проект, который вы можете найти. Могу только предположить, что вы найдете похожий образец (не каламбур).

Чего вы, вероятно, не найдете, так это какой-нибудь отстраненно звучащей абстракции вроде:

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

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

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

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

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

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

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

Идея:

По возможности используйте if-return, а не if-else.

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

Идея:

Объекты в программе должны быть заменены экземплярами их подтипов без изменения правильности этой программы

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

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

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

Почему шаблоны проектирования не работают - или почему SOLID не соблюдает принципы SOLID

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

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

Что такое ТВЕРДЫЙ? пять принципов проектирования, призванных сделать программное обеспечение более понятным, гибким и поддерживаемым.

Как ни странно, я думаю, что принципы SOLID содержат в себе скрытое объяснение того, почему принципы SOLID ошибочны.

А именно, внутри I принцип разделения интерфейса:

Многие клиентские интерфейсы лучше, чем один интерфейс общего назначения.

Мне нравится этот принцип, это единственная часть SOLID, от которой я действительно могу отстать практически в любой ситуации.

Итак, учитывая вышеуказанные цели состояния solid, кажется, что SOLID должен содержать 3 интерфейса:

  • Один, чтобы сделать код понятным
  • Один, чтобы сделать код гибким
  • Один для облегчения сопровождения кода

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

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

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

Что должно быть хорошо, потому что все три вещи хороши… не так ли?

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

KISS (также известная как бритва Оккама)

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

Причина, по которой бритва Оккама / KISS так важна, заключается в том, что сложность ПЛОХО… не по какой-то божественной причине, а просто потому, что наш мозг довольно плохо справляется с этим.

Примечание. Я на 100% убежден, что какой-нибудь читатель может придумать педантичный аргумент относительно того, почему бритва Оккама и KISS - это не одно и то же. Я предпочитаю думать о них как об одном и том же общем принципе, сформулированном для разных областей. Если вы не можете согласиться с этим, не беспокойтесь, мои аргументы не основываются на их неотличимости

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

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

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

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

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

Бимодальное программирование

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

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

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

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

Есть задача поменьше? Хорошо, какие библиотеки я могу использовать для этого? Где узкие места в производительности? Что за сложность здесь написать логику? На какие более мелкие задачи я могу его разделить?

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

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

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

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

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

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

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

Вы замечаете, что используете почти одну и ту же логику в 4 разных местах, вы переносите ее в функцию.

Вы заметили цикл for, который в основном просто выполняет map операцию после того, как вы удалили шаблон, вы заменили его на map.

Вы замечаете часть кода, которая кажется «это собственная вещь»… вы перемещаете ее в собственный файл / класс / объект по вашему выбору.

Вы замечаете бессмысленное имя или тот факт, что вы торопились и использовали k вместо того, чтобы быть более явным и сказать join_k_between_a_and_b или IFUCKINGHATEPYTHONSIO() вместо disable_console_output().

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

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

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

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

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

Помните правила кодирования для крупных проектов с открытым исходным кодом? Единственное, что их объединяет, - это то, что они на удивление расслаблены для проектов, в которых сотни или тысячи разработчиков работают над одним и тем же монолитом.

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

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

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

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

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

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

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

В заключение

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

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

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

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

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

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

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

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

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

Если вам понравилась эта статья, вам также могут понравиться: