Организованное программирование

В этом выпуске я заканчиваю цикл разборов книги "Чистый код" Роберта Мартина. Сегодня подробно обсудим одну из самых противоречивых глав,  посвящённую классам, а также найдем ответ на вопрос - "Почему многие привычные критерии «хорошего ООП» плохо работают в реальной разработке"? 

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

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

Первая часть разбора: https://youtu.be/-tp2bfP36Hg
Вторая часть разбора: https://youtu.be/KK9XK6BtqBM
Третья часть разбора: https://youtu.be/bfhUhim0V1Y
Четвертая часть разбора: https://youtu.be/DqgAqCpYsbs


Подписывайтесь на канал «Организованное программирование» в Telegram: https://ttttt.me/orgprog
– Список подкаст-платформ (Apple Podcast, Google Podcast, Spotify, Яндекс.Музыка и другие): https://podcast.ru/1734325321
– Смотреть в ВК Видео: https://vkvideo.ru/video-224967259_456239231

🔹 Telegram-канал Организованного Программирования: https://t.me/orgprog
🔹Хекслет Клуб в Telegram https://t.me/HexletClubBot
🔹Курсы по программированию — начни учиться уже сегодня: https://ru.hexlet.io/courses


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

Упоминания

Разбор второй части чистого кода - https://bugzmanov.github.io/cleancode-critique/clean_code_second_edition_review.html

Проектирование классов и SRP | Разбор книги Роберта Мартина #5
  • (00:00) - — Введение. Последний выпуск по "Чистому коду"
  • (01:00) - — Что не так с последней главой? Мнение опытного разработчика
  • (06:36) - — Почему “идеальный программист” — это не миф
  • (13:04) - — Как из одной ответственности вырастает оверинжиниринг
  • (23:49) - — Как SRP сбивает с толку, если применять его вслепую
  • (31:12) - — Миф о сложности: почему “много классов” пугает разработчиков
  • (38:44) - — Объекты против классов: главное заблуждение Java-подхода
  • (47:20) - — Как на опыте рождаются архитектурные решения
  • (55:02) - — Анализ циклов и переменных
  • (01:03:49) - — “Чистый код” как манифест устаревшего ООП
  • (01:10:09) - — Увеличение кода ≠ улучшение архитектуры
  • (01:16:16) - — SRP vs. реальность: почему теория не работает
  • (01:22:30) - — Принципы проектирования не работают без контекста
  • (01:27:00) - — Заключение. Что мы поняли о “Чистом коде”?
★ Support this podcast ★

What is Организованное программирование?

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

Друзья, привет. Это подкаст Организованное программирование. Я его ведущий Кирилл Макевнин. Сегодня мы без гостей. И сегодня будет разбор последней главы, которая интересна всем разработчикам, а не только джавистам из книги Чистый код Роберта Мартина. И этой главой мы закончим цикл разбора вообще его книги и её обсуждение, э, вынесение каких-то полезных мыслей или понимание того, что из этой книги либо устарело, либо использовать не стоит. Конечно, вы делаете свои выводы сами, но у меня есть определённое мнение, которым я вот хотел поделиться. И сегодня, собственно, мы закончим эту историю. При этом, если посмотреть на саму книгу, то количество материала, которое там есть, оно сильно больше, чем то, что мы разбираем. Но большая часть этого материала, она суперспецифичная, либо для Джавы, либо прямо какие-то библиотечки разбираются подходы, которые, ну, всем неинтересно смотреть. То, что Мартин рассказывает в классах, достаточно интересно. Обычно многие фокусируют ОП на том, что это о том, как мы проектируем, создаём классы. Это не очень большая глава, но она на самом деле прикольная тем, что мы здесь доразбираем какие-то фундаментальные вещи, которые обычно присущи или их связывают с ОП. Но перед тем, как начать, я буквально пару слов скажу, что, во-первых, естественно, я, когда что-то рассказываю, я либо где-то могу ошибаться, где-то либо могу не договаривать по разным причинам, потому что, ну, не всё можно в таком формате быстро и удобно выговорить. И, конечно же, я читаю комментарии, многие со мной не согласны, понятно, что, а, многих их триггерят. Я частично какие-то вещи, которые наиболее такие сильные цепляют, выношу в в свой Telegram для того, чтобы глубже их раскрывать. Потому что, ну, например, я там про состояния и про побочные эффекты сказал, когда побежал дальше, исходя из того, что, ну, люди понимают, о чём идёт речь. Выясняется, что у многих очень разное представление, о чём говорит Мартин. А мне начинают писать о том, что Кирилл, смотри, он не это имел в виду. Ну и дальше мы, соответственно, где-то там продолжаем. Поэтому такие вещи будут. Я буду их подробнее разбирать. Так что не забывайте смотреть в Telegram-канале, если какие-то дополнительные материалы, связанные с кодом. А второе, когда я уже начал записывать в это время, многие начали говорить, что Кирилл, смотри, Мартин готовит второй выпуск, вторую книжку, и она вот скоро выйдет. На самом деле она уже вышла. И почему про неё говорили, что смотри, мол, там всё устарело, время с тех пор прошло много. Это, по-моему, 2008 год, когда была книжка или 2010, ну, где-то вот в этих годах. И он, скорее всего, вот устаревшие вещи поменял. Поэтому обязательно посмотри, потому что даже те люди, которые были со мной несогласны, местами всё-таки соглашались, что, ну, какие-то примеры действительно старые, но, наверное, просто потому, что они устарели. Сейчас так не пишут. И Мартин 100% думает по-другому в каких-то аспектах, да, потому что я всё-таки читаю его блог и смотрю за его переписками, знаю там примерно, что происходит. Не всегда специально, но так иногда получается. И конечно, мне тоже было интересно, что будет. Так вот, вы должны понимать, что когда вышла эта вторая книжка, он написал, во-первых, пост, в котором сказал такую мысль о том, что всё, что я рассказывал тогда, он не говорил о том, что оно не то устарело, я там поменял подходы. Он сказал, что всё это ещё более актуально сейчас. Я не там отказался не от одной своей идеи или мысли. И всё, что изменилось - это то, как мы это показываем, как мы показываем, почему это намного глубже, намного важнее и так далее, и так далее. То есть концептуально, по крайней мере, он утверждает в том, что все тезисы, которые были в этой книге, они остались. То есть это не соответствует тому, что думали многие ребята, которые смотрели те разборы, говорили, что оно всё сильно устарело. То есть для кого-то устарело, видите, для Мартина нет. И второе, мм, он буквально на днях я видел пост дополнительный, где Мартин тоже как-то приобщается к Иисал, что в эпоху и чистый код ещё более важен для искусственного интеллекта, чем для, в общем-то, людей. Так что скорее он с каждым разом усиливает те тези тезисы, про которые он говорил. Но тут есть интересный момент, поскольку я не единственный, кто делает такой разбор и вообще много с ним и лично в том числе участвовал в дебатах и про это писал, какой-то чувак достаточно известный разработчик, я не помню просто чуть-чуть его имя и кто он, но а он буквально сразу после выпуска этой книги сделал разбор, он его записал как бы и видео в виде видео и можно сказать целый сайт сделал, посвящён этому разбору. То есть там прикольно сделано прямо у тебя слева ра с главы, и он по каждой главе пишет, что с ней не так и какие вопросы она вызывает и какие у него к ней претензии. Я пока не знаю, буду ли я разбирать вторую книжку. Возможно, буду, но скорее всего, поскольку если он говорит, что он ничего не поменял, это будет одним заходом. Плюс, естественно, я воспользуюсь теми материалами, о которых сейчас говорю. Но если вы хотите уже сейчас про это глянуть, соответственно, я приложу ссылочку, можно будет сходить посмотреть, что эти ребята там написали. Ну, а саму книгу вы, наверное, видели либо про неё читали. Но отвечая в очередной раз на вопрос, а почему, собственно, мы разбираем это издание, не то, ну, так вот мир устроен, что несмотря на то, что вышло новое издание, оно не станет таким популярным и его не будут все массово скупать и на основе него делать какие-то выводы и менять свои представления о жизни. То есть в любом случае вот то, что было в том году, тогда эта книга вышла, она так закрепилось в нашем окружении, что её оттуда выбить, ну, очень сложно. Я не знаю, сколько лет должно пройти, чтобы появился какой-то новый стандарт, на который все ориентировались. Хотя, опять же, повторюсь, м это не значит, что не было крутых книжек до этого, после этого и так далее. Просто, ну, так вот жизнь устроена. Что взлетело, то взлетело. И поэтому, как ни крутите, приходится разбирать именно этот вариант, потому что на него все ориентируются. Если все резко перейдут на вторую часть и будут на неё молиться, про неё рассказывать, я сделаю выпуск про неё. Ну и чтобы это не выглядело то, что мы тут проходимся по Мартину. На самом деле, я уже как-то рассказывал, у него есть, например, книжка про идеальный программист, достаточно неплохая, я её всем рекомендую. Но и в целом есть другие книги, которые я обещал разобрать, а которые действительно несут много полезного и хорошего. Особенно хочется это показать тем, кто считает, что кроме Мартина не было хороших книг, которые собрали практики программирования. Их было, они есть. И, естественно, я такие разборы делать буду просто чуть-чуть попозже. Сейчас вообще, кстати, последнее время заметили, возможно, чуть больше было пропусков с моей стороны, потому что я очень глубоко ушёл в кодинг, плюс праздники там болезни семьи, всё такое. Ну, дети, зима, всё понятно. И поэтому у меня немножко сбился мой ритм, которым я обычно всё делаю. Поэтому я к этому моменту снова возвращаюсь и продолжаю с тройным усилием снова радовать вас классным, полезным контентом и рассказывать интересные штуки. Ну и делать, соответственно, подкасты. Давайте мы разберём эту главу, а я немножко про неё расскажу. Так, а значит, классы. По стандартным правилам Java класс должен начинаться со списка переменных. Сначала перечисляются открытые статические константы, далее следуют приватные. Ну, он просто рассказывает про порядок. Честно говоря, не принципиальная штука. Не в том смысле, что на это не надо обращать внимания. Просто обычно такие вещи даже делаются автоматическими инструментами, да, которые просто сами сортируют в правильной стране. И чем дальше, тем больше на такие штуки никто не обращает внимания. Поэтому вот про этот аспект можно сказать, что он действительно немножко устаревает. Не в плане самой рекомендации, а в плане того, что это очень минимальное влияние оказывает на происходящее. Во-вторых, чаще всего автоматические инструменты. В большинстве языков это сортируют в нужном направлении. Как вы понимаете, PR, наверное, был один из первых таких мм форматоров для JS, который это очень сильно популяризировал. И, соответственно, дальше это разнесли по всем языкам. Поэтому так или иначе вот этот порядок он более-менее определяется. Но правда мартинг идёт ещё дальше и рассказывает про вещь, которая в принципе очевидно любому разработчику, но она с перестаёт действовать с определённого момента. Это про то, что вы ближе к верху располагаете функции, которые такие наиболее там с точки зрения абстракции более высоко высокоуровневые, потом, значит, менее, и в конце там вообще всякая мелочь идёт. И это относительно работает. Пока там маленький класс, не очень много кода, но в какой-то момент всё равно вы не сможете поддерживать красоту на этом уровне, на постоянке, чтобы у вас вот прямо так было. Тем более, честно вам скажу, э в данном случае гораздо важнее классный система навигации по классу, например, которая позволяет по функциям, по символам прыгать, потому что, да, конечно, приватные там должны быть внизу публичные сверху, но вот с точки зрения того, что мы сейчас публичные методы будем ещё сортировать в каком-то порядке, я бы не стал этим заниматься. Оно не имеет никакого смысла. Это такое перфекционизм уже за гранью добра и зла, который никому особо не нужен. Но если кому-то нравится, пожалуйста. Так, инкапсуляция. Мы предпочитаем объявлять переменные вспомогательные функции приватными, но относимся к ним без фанатизма. Не совсем понятно в каком смысле что значит фанатизм без, потому что он всегда какой-то свой смысл закладывает. Но поехали дальше. Иногда переменную или вспомогательную функцию приходится объявлять защищённой, чтобы иметь возможность обратиться к ней из теста. Так, с нашей точки зрения, тесты исключительно важны. Здесь два момента. Первое, поскольку я не привык и думаю, большинство разработчиков не привыкли к русскоязычной терминологии, потому что это редко говорится, что переменная или там функция защищённая, говорят обычно протеed, то есть мы прямую кальку используем. И он фактически здесь предлагает, если кто знает, есть такой антипаттерн паблик морозов. Я про него иногда в видео рассказываю. И вот то, что он предлагает, очень похоже на это, да, когда, допустим, кто-то хочет протестировать кишки м какого-то класса, то его внутренние члены делаются протеed, и потом от них мы просто наследуемся от этого класса и, соответственно, наследник получает доступ к протектоду, а у себя мы это делаем пабликом. И таким образом мы получаем доступ к внутренним частям. Честно говоря, то, что вот он здесь говорит, это я сначала прочитал, когда я подумал, может, он сейчас как-то скажет, что так неправильно делать и это не то, но, честно говоря, не нашёл этого, потому что он просто эту мысль дальше раскрывает и как бы говорит о том, что, конечно же, в первую очередь надо с приватными делать. Но иногда бывает так, что вот приходится делать так, вот он пишет: "Ослабление капсуляции должно быть последней мерой для того, чтобы, собственно, иметь возможность в первую очередь, наверное, протестировать". Да, честно скажу, я не вижу ни одной причины, по которой можно было бы внутренние кишки открывать, если они реально внутрение. То есть тестировать, конечно, надо публичные интерфейсы. Если у вас есть необходимость тестировать какую-то какую-то внутренность, вот как он пишет, что это последняя мера, вот прямо полюбас больше никак, то скорее всего означает, что внутренний код настолько важный либо сложный, либо хитрый, и он требуется своей собственной абстракции, его надо выделять. Но вот представить, что у вас есть, допустим, всего лишь один класс, внутри которого есть какая-то логика, и вам прямо вот жизнь необходимо полезть внутрь, чтобы протестировать что-то прямо глубокое, и для этого вам приходится прайвать на протект отменять. Хмм, у меня большие вопросики возникают, и меня немножко удивляет, что он это поддерживает, потому что как будто бы на протяжении всего предыдущего таких разговоров не было. Ну, может быть, я ошибаюсь. Всё-таки уже какое-то время прошло, хотя эту книжку ведь я уже много раз перечитал для того, чтобы вам её здесь рассказывать. Поехали дальше. Классы должны быть компактными. Тут он повторяется полностью и делает это видно специально. Первое правило класса должны быть компактными. Второе правило класса должны быть ещё компактнее. Дальше нет, мы не собираемся повторять текст из главы три, хотя на самом деле уже повторил, пытаясь нам в мозг вбить эту мысль. Ну и как в случае с функциями, компактность должна стать основным правилом проектирования классов. А для классов начинается с вопрос: а насколько компактными? Размер функции определяется количество физических строк. В классах используется другая метрика. мы подсчитываем ответственности. Я, наверное, так скажу сразу, то есть тут мы уже это на уровне функции обсуждали, но я ещё раз хочу это подчеркнуть. Вот он ниже приводит пример супер dasшборд, представляющий около 70 открытых методов. Ну и как бы показывает просто всё внутри подряд. Я в какой раз уже говорю, что здесь, ну, какая-то манипуляция происходит, потому что это так называемый G object, да, когда у вас просто есть класс, который вообще всё пихает, но это уровень, ну, совсем прям не тот. на котором мы рассуждаем, как бы, когда мы занимаемся разработкой. То есть, если человек вот так делает, то там речь идёт вообще не о классах, там речь идёт о том, что уровень программирования в целом, понимание, что такое абстракции, вообще как бы что такое программирование, что такое код, там надо на другом немножко уровне разговаривать с человеком, для которого вот это нормально. И он сделает из этой книжки вывод, что ничего себе, я думал так код нормально писать, оказывается, так код писать нельзя. Поэтому, мне кажется, он всё-таки перегибает здесь палку. В любом случае люди понимают, да, у вас классы даже естественным образом. Вот есть там класс такой-то, класс такой, то есть какие-то смыслы туда закладываются. А вот дальше у человека, конечно же, естественным образом, то есть я не представляю себе человек, который не думает о том, что, хм, на какие же классы это разбить, если он так делает, да? То есть он такой будет пихать всё в один класс и думать, как я классно делаю, я вообще не сомневаюсь в том, что я крутой. Так не бывает. Наоборот, скорее бывает, что человек чувствует и понимает, что надо разбивать, и ему надо разбивать. Поэтому он приводит, конечно, совсем безумный пример. А вот дальше история про то, что нужно типа совсем компактно, совсем разбивать. Здесь причины следствия перепутаны. Я сейчас ещё несколько раз про это скажу. Он постоянно говорит по поводу количества ответственности. Вот мы сейчас это обсудим, потому что здесь скорее наоборот немножко по-другому надо делать, потому что если вы таким образом будете мыслить, то вы, в принципе, будете думать только о классах. Хотя на самом деле нужно, когда составляете программу, не о классах думать. Ну представьте, просто вы специалист, да, вы действительно программист. И вы приходите в язык, в котором нет классов. Это что означает? Сразу вы не способны писать код или типа если нет классов, значит там нельзя писать нормальные программы. То есть возникает очень много вопросов к тому, что всё-таки программирование - это дисциплина инженерная или всё-таки есть специалист по Битриксу, есть специалист по Джаве, но при этом специалист по Джави ничего не понимает, например, там в Рубикоде и так далее. Очень много вопросов вызывает вот такие формулировки. Ну, понятно, да, в любом случае, он просто тупо показал класс, даже название какое-то безумное ещё и понятно, что frame при этом имплементит метод data user, но ну нельзя в в здравом уме твёрдой памяти такое написать. Это так не работает. Ещё и 70 методов. Вот поэтому тут даже бессмысленно обсуждать. Это передёргивание называется. Вот. А, ну и при этом ещё и супер дашборд называется. А если он вообще про внешний вид и должен что-то показывать, так там вообще не должно быть ничего такого. Это, кстати, ещё одна проблема, которая часто на протяжении всей книги возникает. Он показывает какой-то пример, который очень сложно привязать к реальности, но он выглядит как будто правдоподобно. А ты начинаешь думать: "О'кей, вот я пишу фнд, я пишу бэкэнд, там у меня есть опыт написание фреймворков, есть опыт написанием, есть опыт написание, ну, разных всяких систем". Я понимаю, что вот то, что он показывает, туда не притянуть. У него был такой же, помните, пример, когда он там про систему контроллера управления, значит, датчиками там делал. И мне в комментариях же тоже потом люди писали, что это вообще так не пишется. То есть он какие-то примеры взял из своей головы, как бы не имея понятия о том, как это делается в настоящей системе. Ну либо, может быть, ещё что-то всегда можно придумать, да, но оно выглядит очень странно. И поэтому, глядя на этот код, ты не можешь сказать, это вообще нормальный код или нет. И здесь то же самое. То есть я, когда такое вижу, да, супер дашборд, я не очень понимаю вообще смысловую нагрузку, потому что если он как бы говорит про экран, который он хочет показывать, так это вообще штука не имеет отношения к тому, какие там методы. То есть если мы же не 100% не с нуля пишем, да? Мы в каком-то фреймворке пишем, а значит, там просто будет чётко и понятно, что вот эта штука отвечает за рисование. Вот здесь у нас, соответственно, данные хранятся и так далее. Это базовый минимум, который предлагает, ну, любая система. Сами, конечно, такие вещи с нуля, ну, не пишут. Поэтому для того, чтобы создать вот то, что он показал, это прямо надо специально сесть. И даже если вы как бы разработчик там, не знаю, с годовым опытом, прямо такой: "Как мне сделать так, чтобы напортачить?" Вот примерно такое должно быть мышление, чтобы получилась такая вот имплементация. Вот, что уж говорить, если смотреть на название методов и что внутри происходит, но несерьёзно, да? Дальше он говорит: "А если бы класс супер Dashшборд содержал только метод, приведём в листинге 102 и показывает". Значит, э, во-первых, самое главное, что с точки зрения наследования имплементации он ничего не поменял. Хотя в реальности, вот если уж мы как бы в рамках этой концепции размышляем, как он предлагает, это первое, что мы должны были обсудить. А то есть, а что он вообще, что он из себя представляет, что он наследует, почему он имплементирует метод data usеer. То есть тут вопросы вот какие первичные вообще, о чём мы говорим, что это такое, а здесь этого нет. И он дальше, соответственно, предлагает другую штуку. Он говорит: "Давайте вот эти вот методы: get last focus, component, set last focused, get major version number, minor, get build number." И я могу, конечно, посидеть и подумать: "А почему именно этот набор? Почему именно это, да? Например, там с фокусировкой, что если действительно это экран, у него там фокус и все дела". Но в целом, во-первых, это не проговаривается, во-вторых, не поменялось с точки зрения наследования и имплементации ничего. Во-вторых, я не очень понимаю. Ну, о'кей. Фокус - это, как правило, ещё и какая-то встроенная функциональность, да, когда у нас там фокусируется что-то. Но причём здесь версия, причём здесь build number? То есть как можно вообще рассуждать о классе, не понимая, в рамках какой системы мы это делаем. Вот. И дальше он ещё говорит, что 5 МО не слишком много или не так ли? В нашем случае слишком, потому что, несмотря на количество класса по-прежнему имеет слишком много ответственностей. Опять о чём мы вообще говорим, какая что он должен делать? Какую вообще проблему мы решаем? Почему у нас, в принципе, существует такой класс? Мм, и в итоге, а, здесь происходит, по мне, подмена понятий. Вместо того, чтобы размышлять сначала о смысле, чтобы посмотреть на систему, о том, что она из себя представляет, в рамках какого архитектурного подхода мы это рассматриваем, потому что, например, у Реакта там подход один, у каких-то других фреймворков бывает другой, то, как они, соответственно, там с экранами работают. Опять же, я не могу себе представить, что мы здесь говорим не в рамках какого-то визуала, да, супер das дашбор там, все дела, поэтому надо понимать, какая это штука. Я думаю, кстати, джависты сейчас бы мне сказали: "Вот, Кирилл, смотри, там наследуется от Jframe. Это значит какая-нибудь там джавовая приблуда, с которой я не работал никогда". И вот, соответственно, там вот так это всё решается. Но опять же, я уверен, вы бы тоже сказали, что там и в доке, и везде написано, как это разделять, и какие методы там должны быть, каких методов не должно быть. О'кей. И он продолжает вот эту историю про то, что много методов, мало методов, много методов, ну, немного. Вот если у вас супер dasшборд, то есть мы не подвергаем сомнению, что супер dasшборд - это нормально, а он вроде как не подвергает, то, очевидно, у вас там, допустим, будет выводиться дофигища компонентов разных и, естественно, будет дофигища методов, но они все будут одноуровневые. То есть они все будут относиться просто к, грубо говоря, вы не можете уменьшить их количество, если вы не уменьшите количество элементов на самой страничке. Поэтому количество методов в этом плане, оно всегда отражение смысла. Ну то есть если у вас действительно много всего, а вы такие: "Нет, мне надо только три метода, вот больше нельзя". Но вам придётся просто весь дэшборд урезать, там останется один какой-нибудь элементик маленький, на котором есть там, не знаю, пару способов с ним взаимодействовать. Опять же, я сейчас фантазирую просто исходя из смыслов, которые я додумываю, потому что он их не даёт. И, мм, поэтому, а, негативный эффект, который в такой даже подаче и по мне манипулировании вот сознанием нашим, он в том, что из-за отсутствия объяснения действительно теряется эта нить. То есть, если себя не останавливать критически, не мыслить, очень легко попасться на эту удочку и в эту ловушку и говорить с ним, поддагивать ему и говорить: "Да, да, типа, всё правильно, так и должно быть, надо вот так вот методы просто резать". Если вы не согласны, конечно, об этом напишите. Правильно ли вы считаете, вот он подошёл к тому, как надо разбираться с гот обжектами? Имя класса должно описывать его ответственности. В сущности, имя класса должно стать первым фактором, способствующим определению размера класса. А спорно, да? Я бы не согласился здесь. А если для класса не удаётся подобрать чёткое, короткое имя? Вероятно, он слишком велик. Ну, потому что, опять же, это очень просто говорить, подбирая какие-то примеры. А вот, например, человек никогда в жизни не работал там с парсерами, ещё с какой-то низкоуровневой штукой, которая может быть очень объёмная, чисто из-за вот логики, которая внутри, которую, ну, нельзя убрать, потому что вот тема такая, она очень, например, компактная, как абстракция, очень изолированная, но кода просто тупо будет много, потому что мы там байтиками начинаем оперировать. Вы откроете на гитхабе какие-нибудь классы, занимающиеся вот каким-то таким разбором и чем-то в этом духе. А и вдруг оказывается, что кода там дофига, а сопоставить это с названием невозможно просто потому, что вы с этой темой не были никогда знакомы. Но открывать како-нибуд там Jon Parcer, вот какое у вас представление, сколько там должно быть кода? Я уж даже не говорю по поводу производительности и написания в таком аупшном стиле. То есть это очень очень такая однобокая правда. То есть можно, конечно, это притянуть к тому, что когда я говорю там про, ну, давайте так, вот у меня есть user DB Connection, как название классов, ну да, погрес SQL, допустим, говорит об объёме кода. Мм, я бы не согласился. Если для класса не удаётся подобрать чёткое короткое имя, вероятно, он слишком велик. Чем туманнее имя класса, тем больше вероятность, что он имеет слишком много ответственностей. В частности, присутствие именно классов слово процессор менеджер супер час свидетельст о нежелательном объне ответственности. Это правда, но это опять же это такие ивристические критерии, по которым можно уже воспользовавшись сначала какими-то подходами по организации кода, мы уже опосле осмотрим. Мы такие можем попытаться, ну, оценить, может быть, мы действительно что-то не так сделали и у нас есть что-то тяжёлое для восприятия, тяжёлое для работы. Ну, но не мы не смотрим на это с точки зрения количества методов, а просто скорее тут надо про абстракции думать. Но поскольку здесь он этого пока не цепляет, не будем об этом много говорить. Краткое описание класса должно укладываться пример 25 слов без выражений есть, и или но, как бы вы, как бы вы описали класс супер дэшборд. Класс представляет доступ к компоненту, который последнему имел фокус ввода и позволяет отслеживать номер версий и сборки. Первое. И указывает на то, что супер dasшборд имеет слишком много ответственностей. Здесь какой-то чат GPT стиль. То есть, фактически, мы не про смысл говорим того, что такое дашборд, а он просто смотрит, что там внутри написано, и как бы перечисляет, что а ещё он делает вот это, вот это, вот это. Но при таком подходе, какие бы методы мы не не отобрали вот из его первого списка, из семи0сяти методов, оно бы вот превратилось в и и и, потому что вот такой формат он выбрал. Поэтому, если мы описываем, оно, конечно, описывалось должно быть на другом уровне, что, ну, вот дэшборд представляет собой дашборд, на котором выведены там какие-то, а, графики, допустим, да, и они относятся, ну, поскольку он супер, ну, например, там основные графики со всех там подсистем нашей системы. Тоже так немножко абстрактненько звучит, но раз супер, значит, это логично. Ну, и так далее. То есть всё равно это было бы по-другому. Дальше он резко переходит к принципу единой ответственности. Причём почему-то, мне кажется, мы это уже обсуждали. И в рамках функции об этом говорили, но допустим, нет. Давайте ещё раз. Принцип единой ответственности утверждает, что класс или модуль должны иметь одну и только одну причину для изменения. Это принцип даёт нам как определение ответственности, так и критерии для оценки размера класса. Классы должны иметь одну ответственность, то есть одну причину для изменений. Перед тем, как идти дальше, у меня есть некое представление об этом принципе, которое я с собой через всю свою жизнь несу, потому что начал я заниматься разработкой примерно, когда он выпустил эту книжку. И тогда я, естественно, не просто всё это слышал, но ходил про это, рассказывал, пропагандировал и вместе со всеми поддакивал, что дада-да. Но с опытом, наблюдением за другими людьми, за кодом, за всем остальным, я заметил очень простую вещь, а касаемо этого принципа, а это легко видеть в комментариях. Он очень хорошо работает. А после вот когда вы разбили систему уже по каким-то там своим соображениям и начинаете применять этот принцип, то вдруг оказывается, что он очень хорошо на неё ложится. То есть вы такие: "Смотрите, у меня система разбита". Но изначально проблема в том, что он очень косвенный. Он не про смыслы, он не про действительно то, как система имеет смысл разбивать на основе, там, не знаю, того, как работает HTTP. То есть, если вы берёте очень многие области, там выясняется, например, как делать интерпретатор, там, как делать компилятор, там выясняется, что есть просто очень чёткие, понятные десятилетиями, годами сложные абстракции или зоны, которые обычно имплементируют независимо. Если вы бы это делали не на плечах гигантов, потому что уже видели, как это реализуется, или там какие-нибуд библиотеки для построения интерфейсов, да, то если бы вы делали это с нуля и такие вот ва этого ничего не показывали, вы бкндер, вам говорят: "Вот сделай библиотеку для фронтэнда". И вас посадили бы её делать, сказали: "Вот надо создать классы, чтобы вот всё было такое классовое". И оперируете только этим принципом. Какова вероятность, что у вас бы получилось что-то действительно вменяемое? Вот я скажу сразу нулевая, потому что он здесь абсолютно вторичен, потому что тут не о классах надо думать вообще. Это совершенно на другом уровне происходит мышление. И только потом уже в какой-то момент вдруг оказывается, что если мы про этот принцип начинаем говорить, смотрите, волшебным образом он совпал. Ну, потому что это какая-то, ну, грубо говоря, корреляция, и она как бы следует. То есть если вы исходите из неё, не понимая то, с чем вы работаете, или как бы делая систему первый раз учать в процессе, да, не сотый раз, конечно, да, у вас может быть получится много маленьких классов. практически готов гарантировать, что суть этих классов и то, как они соединяются, будет иметь малое отношение к хорошему дизайну. Вот. Дальше он показывает небольшой, казалось бы, класс супер das Dashшборд в листинке 102 имеет две причины для изменения. Во-первых, он отслеживает версию, которая, вероятно, будет изменяться при каждом обновлении продукта. Я тоже, ну, всё равно не очень понимаю, кому может прийти в голову идея в класс, который отвечает за отображение, не знаю, дашборда, версию продукта именно к нему прицепить. Это надо очень постараться. Во-вторых, он управляет компонентом Java Swing, потом класса Jframe, представляющего графическое окно верхнего уровня в Swing. Сорян, я сразу не понял, что Jfame - это свиing. Со свиingм, естественно, я знаком, на нём не писал, но понятно, что это библиотечка для построения, собственно, интерфейсов. И, кстати, на ней много чего сделано. То есть эта штука, если я не ошибаюсь, может могут меня поправить. Типа всяких эклипсов, нетбинсов. И что же ещё там такое было? Гимп что ли, по-моему, сделал. Ну, в общем, много старых разных программ линксовых, особенно они сделали на свинге. Несомненно, номер версии должен обновляться при любых изменениях кода swing, но обратно не всегда верно. Номер версии также может измениться вследствие изменений в другом коде системы. По-прежнему не понимаю, какое отношение номер версии нашего приложения имеет вообще к вот этому всему, потому что это просто не на этом уровне должно рассматриваться. Попытка идентификации ответственности и причин для изменения часто помогают выведеть и создать более качественные абстракции для вашего кода. Все три метода супер дэшборда, относящиеся к версии, легко выделяют свой отдельный класс с имене. Класс обладает хорошим потенциалом для повторного использования в других приложениях. По мне, это overengineнингринг в чистом виде. Если бы я работал с человеком, у которого вот действительно такой код, я в первую очередь бы спросил: "А какое отношение вообще версия имеет к отображению?" Потому что, если мы говорим про версию нашего приложения, просто тут никаким образом проявляться не будет. Если её просто где-то надо показывать, но тогда это ничем не отличается от любых других данных, которые мы просто показываем. Значит, у нас есть какой-то механизм, в принципе, где у нас данные хранятся и где мы эти данные берём, и уж точно там нет никакого там изменения, например. И в этом плане версия ничем не отличается от любых других данных, да. Поэтому то, что у нас появляется какой-то такой класс, ну мы же туда, откуда-то эти данные должны передать, то есть получается, что проблема как бы переносится ниже. Она же всё равно где-то есть. И чаще всего это вообще всё бессмысленно, потому что есть просто некий набор данных, который хранится по определённым принципам того, как state-менеджмент реализован в вашей юайной библиотечке. И всё, вы это туда положили и просто оттуда забрали в какой-то момент. Поэтому вот так, что у нас появится класс вершина, мы такие радостные сидим, что смотрите, какая классная штука для переиспользования ещё появилась. Это маловероятный сценарий. Принцип единой ответственности одна из самых важных концепций в объекноритированном проектировании. Кроме того, если его относительно несложно понять и соблюдать. Но, как ни странно, в принципе, единая ответственности часто оказывается самым нарушаемым принципом проектирования классов. Мы постоянно встречаем классы, которые делают слишком много всего. Почему? Заставить программу работать и написать чистко-д совершенно разные вещи. Ну, здесь дело не в том, что чистый код. Дело в том, что понимание вообще абстракции, того, как можно спроектировать и в какую сторону будет развиваться продукт, это не просто сложно, это ещё и не детерминировано. То есть нету одного какого-то пути развития, нет какого-то одного способа разделения, который будет вот классным. Всё очень сильно зависит от того, в какую сторону пошёл ваш продукт, потому что один продукт там может быть, я не знаю, мы должны быть в суперкроссплатформенный запускаться везде. И поэтому сам свинг там реализован, допустим, как абстрактная фабрика, чтобы там UI менять в зависимости от того, здесь одна библиотечка, там другая, и порождаются, например, какие-то абстракции, которые пытаются всё это обобщать. Кто-то вообще концептуально по-другому идёт, что у вас просто там подмена имплементации идёт или, например, мы делаем библиотеку, а это единая библиотека, но для, например, разных там кейсов мы используем разные элементы. То есть типа для Андроида, как React Native, например, да, это будут одни элементы, для iOS - это другие. То есть получается, знаете, это есть концепция learn ones, use everyw, потом write ones. Короче, понятно, о чём речь идёт. То есть либо одна система, единый подход, но при этом под каждую целевую систему вы пишете свой код, используя это единое ядро, либо, наоборот, она пытается абстрагироваться, и тогда вы пишете типа один код, который там и там работает, но, соответственно, вы платите за это определённую цену. Это дополнительный слой абстракции. При этом, естественно, всегда есть различия, с которыми вы сталкиваетесь, и вы страдаете. Java, кстати, пошла именно по этому пути, и она в этом плане обломалась. То есть это сработало для запуска самой Джавы, но не сработало для, например, инструментов, которые на ней пишут, потому что они выглядят либо стрёмно, неэффективно работают. И в итоге подход, который, например, использует React Native, я привожу пример именно этот, потому что мы в контексте UI говорим, он выбрал подход в отличие Самарина, например, какого-нибудь, да, что мы у нас есть типа React Ntiative, у него некая концепция того, как он работает. Но если вы пишете приложение под Android, вы пишете свой код. И под iOS вы пишете свой код. Да, у вас могут быть общие части, которые переиспользуются. Они как раз UI независимые в первую очередь, но при этом интерфейсы вот они как бы свои, но при этом концепт один, да, он такой типа реактообразный. Вот поэтому говорить о том, что у вас классы вот такие сели, подумали и разделили или вы такие, исходя из чистого кода это делаете, это вообще неправильно. Это всё зависит от того, в какую сторону вы абстракции развиваете. Например, если вы делаете database access layer, то у вас там, естественно, будет идти в сторону появления новых адаптеров для разных баз данных, и у вас это будет основная часть как бы такая подмена. Если вы работаете с какой-то конкретной базой суперспецифической в вашем коде, который вы вообще сами написали, как какой-нибудь Facebook, который под себя пишет, естественно, у него не будет попытки строить такую библиотеку для взаимодействия с этой базой, делая её расширяемой под другие базы данных. Ну, если она, например, специфическая какая-нибудь, да, и, соответственно, они будут делать акцент вообще на другое. Здесь они просто сделают эту либу, она будет нерасширяемая, но она будет супер эффективно работать с конкретной вот этой базой данных. И дальше они просто занимаются какими-то другими частями своего приложения. Вот такие дела. Поэтому здесь, говорю, прямо какая-то подмена понятия постоянно идёт. Попытка не через суть э того, что мы пишем и в какую сторону мы это развиваем и почему нам где-то в одном месте нужны абстракции, где-то можно деревянный код писать. Он вот меняет это на то, что давайте через размеры, через наши представление о прекрасном смотреть, как мы будем разделять. В то же время многие разработчики опасаются, что множество небольших узко специализированных классов затруднит понимание общей картины и беспокоит то, что им придётся переходить от класса в класс, чтобы разобраться в том, как решает одна более крупная задача. Однако система с множеством малых классов имеет не больше подвижных частей, чем система с несколькими большими классами. Последние тоже придётся разбираться, это будет ничуть не проще. Так что вопрос заключается в следующим. Хотите ли вы, чтобы ваши инструменты были разложены по ящикам со множеством небольших отделений, содержащих чётко определённые и подписаны компоненты, или вы предпосчитаете несколько больших ящиков, которым можете свалить всё подряд. А проблема с любой аналогией в том, что аналогии создают иллюзию понимания и ведут вас обычно не туда. Вот кажда любая аналогия про то, чем является программирование, там сад, это строительство и так далее, не соответствует тому, чем является программирование, и запутывает людей. Здесь ровно то же самое. Аналогия с ящиками классная с точки зрения того, что так можно людям вбить свою идею, которую вы хотите вбить. Они такие: "О, действительно, по ящичкам разложить, по смыслам очень легко". Но в программировании это немножко не так работает. И, как правило, очень легко это видеть. То есть когда делаете что-то новое, почти всегда, особенно на гитхабе, создатели репозиториев прямо пишут: "Прямо сейчас мы разрабатываем всё в одном репозитории". Потому что если мы прямо сейчас разнесём, может выясниться, что, во-первых, это сложно синхронизировать. У вас появляются проблемы, которых просто тупо раньше не было. у вас появляются проблемы синхронизации. На уровне микросервисов - это вообще страх, а на уровне кода - это хотя бы вот версии там сопоставить. Вы уже быстро что-то не поменяете, чтобы это не, ну то есть типа в одном месте поменял и чёрт знает, сколько време пройдёт, пока в другом месте это можно будет применить, там процесс релиза, деплоя. Короче, в конечном итоге большинство людей, которые разрабатывают какие-то новые штуки, где они не до конца уверены, они всегда делают это в одном месте и прямо пишут об этом. Мы сейчас делаем так, а потом разнесём. Те ребята, которые сразу начинали с того, что давайте всё разнесём, они редко вообще до продакшна доводили. У меня есть прямо кейсы и примеры, когда вот такой подход к чему приводил. И кейсы того, как многие успешные проекты сначала были цельным куском, которым с опытом было понимание, потому что понимали кейсы, куда он развивается, выносили какие-то части, и они становились независимы. И тогда уже легко было говорить, что смотрите, у нас тут всё по единой ответственности, да, но извините, сколько к этому мы шли и через какие ошибки а мы прошли. гораздо важнее скорее это граница абстракции, изоляции. То есть где у нас барьер, где вот дальше лезать нельзя. Я как пример приведу, опять же, поскольку здесь UI затрагивается, классный кейс с реактом, например, и компонентом юайными. Вот, допустим, есть действительно отдельный компонент, который там Markдаунредактор. Вот когда вы прямо на текстаре вешаете и у вас там прямо marкдаун. С точки зрения нас как пользователей, мы явно хотим один компонент, которым легко управлять. Он там позволяет, как правило, это он какой-нибудь, да, на изменение. Мы можем данные оттуда получать и внешним видом как-то, может быть, немножко мы можем рулить, хотя по первой опять же может не обязательно. Главное, чтобы работал хорошо. Ну и соответственно, что он к формам может цепляться, можем там с ним работать. Но по большому счёту мы хотим один компонент. Вот он работает и всё. Вообще-то Markдауредактор не самый простой компонент, потому что там действительно может быть много логики, много всякого разного, особенно есть всякие инсерты, а есть уж ещё картинки перетягивают, так вообще целая история. И внутри, скорее всего, для человека, который, например, сделал этот компонент своим проектом и его разрабатывает много лет, там, конечно, будет много всякого разделения. Но если вы делаете это вот так сейчас первый раз от нуля для себя или просто что-то хотите поиспользовать, вам, во-первых, вообще без разницы, что там внутри, оно не влияет на вашу внешнюю часть и не копит никакого долга технического, потому что с течением времени, чтобы не происходило внутри, оно никак не отражается наружу. Если у вас вот этот как раз барьер абстракции, то есть именно тот способ взаимодействия с внутренностями этого компонента не выходит за рамки там правильных пропсов, которые всегда примерно одни и те же, независимо от того, как внутри всё это устроено. И в таком смысле нет никакого смысла с самого начала пытаться что-то выделять, пока вы не упёрлись в то, что ой, больно, лучше разделить, особенно если вы над этим работаете. Но если уж прямо совсем современность притягивает, то, честно говоря, всё ещё проще, потому что каждый раз, когда, например, у меня возникают такие задачи, я не могу найти готовое решение, мне просто такие компоненты пишет чат GPT и на выходе это просто законченный кусок кода, там, допустим, на 500 строк. Мне даже в него смотреть не надо, потому что самое главное, что я знаю - это интерфейс, с которым я взаимодействую. И следующая переработка - это просто типа сгенерируй мне его с нуля целиком. Кто-то сейчас скажет: "Кирилл, но тогда не было чат GPT и так далее". Это уже просто ускорение. Но концептуально просто если есть вот эта граница абстракции, то неважно, каким образом получен тот код, и неважно, как он разделён, он не растит общую сложность проекта, он не растит техдолг, потому что это изолированная функция, это изолированная штука, которую вы, может быть, вообще тупо выкиньте, замените на готовое решение. Вот так вот. Поэтому у меня очень-очень сильно другое к этому как бы всему отношение. Ну, поехали дальше. Каждая крупная система содержит большой объём рабочей логики и обладает высокой сложностью. Первойочередной целью управления этой сложностью является формирование структуры. Он это здесь подчёркивает. При которой разработчик знает, где искать то, что ему требуется. В любой момент времени может досконально знать только ту часть темы, которая непосредственно относятся к его работе. Напротив, в системе с большими многоцелевыми классами нам неизбешно приходится разбираться со множеством аспектов, которые в данный момент нас не интересуют. Ну, вообще большая система, она же из ниоткуда не берётся, так уж если посмотреть на это. Поэтому, если мы уже к этому пришли, то мы прошли через какой-то количество итераций. Но в целом тут, наверное, особо нечего обсуждать, потому что формирование структуры, что это основное управление сложностью, но это не так. И тем более он это сам прекрасно знает и про это говорит о том, что у нас есть проектирование, где мы определяем, что и как. Честно говоря, это всё-таки не совсем структура классов. Скорее, я бы сказал так, это всё-таки формирование каких-то есть архитектура на уровне, например, допустим, слоёв. Мы понимаем, какие у нас слои, в рамках какой вообще концепции мы работаем. И это сильно шире, чем просто какие-то классы, да. Если он, например, взял тот же сwing, ну он предоставляет уже абстракции. У вас есть там кнопки, элементы, как их объединять. То есть это не то, чтобы с нуля берётся. Если речь про бизнес-логику, там другая история. Если речь про какие-то бэкэнф- фреймворки, что чаще всего используется, у вас тоже там есть какой-то минимальный набор абстракций, который уже много чего даёт. Поэтому нет такого, что классы добавляются там огромными кучами, как целые подсистемы. То есть понятно, что там надо что-то реализовать, пошли и сделали. Вот поэтому сложно себе представить такую систему, при которой нужно сесть и сразу с проектировать и реализовать какое-то гигантское количество классов. или вообще не проектируем, а такие сразу: "О, нам надо, не знаю, там пять классов, и в них будет там по 100, ну ладно, 100.000 - это, конечно, много, ну, допустим, 5-10.000 строк кода, но, честно говоря, так не бывает. Ещё раз выделю основные моменты. Стем должно состоять из множество мелких классов, а не из небольшого числа больших. Каждый класс нкапсулирует одну ответственность, имеет одну причину для змеи, взаимодействия с другими классами для реализации желаемого поведения системы. А я повторю ещё раз, что ориентироваться на количество и размеры классов при проектировании систем вообще не надо. Это вторично по отношению к тому, как у вас система разделена на какие-то независимые домены. В ДДD любят говорить слово баounded контекст. Ну вот что-то в этом духе. Как у вас слои организованы, взаимодействие между ними и так далее. Кстати, если послушать всяких разных людей, особенно тех, кто адепты там ООП такого больше по Аланакею, ну или вообще про проектирование, очень часто говорят, что вот это вот классовое ориентированное программирование и часто противопоставляют, кстати, объекты ориентированны программированию, по какой-то причине люди решили, что класс - это самое главное, при том, что главное - это на самом деле не класс, а обмен сообщениями. Я не сказал бы, что тоже придерживаюсь этой точки зрения, но действительно есть перекос в то, что вообще вся система при таком подходе, как её описывает здесь нам Мартин, она как бы теряет смыслы. То есть мы просто мыслим только как будто бы классами. И явно это не то, как нужно вообще думать о коде, потому что это какой-то такой прямо супер Java специфический подход. Я, например, разрабатываю, когда на Тайпскрипте и пишу разные системы. Например, на Хеслите есть редактор, да. Это, ну, в общем-то, тоже интересная штука, которая сделано полностью, ну, вот нами, да. То есть там есть, конечно, внутри какие-то элементы, но в целом довольно много прикольных штук. Там табы всякие, синк с бком, соответственно, к файловые деревья и так далее, там и взаимодействие, много интерактива, много всяких автосейвы. Если вы меня спросите, сколько классов в вашей системе и состоит ли она из множества маленьких классов, я скажу, что, по-моему, там, конечно же, Реакт, но в целом, по-моему, там ни одного класса нет. Если и даже есть, это никакое отношение к архитектуре этого проекта не имеет. Ну, так что с этим всё чуть, видите, проще, чем то, что он здесь описывает. Поехали дальше. Классы должны иметь небольшое количество переменных экземпляров. Каждый метод класса должен оперировать с одной или несколькими из этих переменных. В общем случае, чем с большим количеством переменных работает метод, тем выше связанность этого метода со своим классом. Класс, в котором каждая переменна используется каждым методом, обладает максимальной связанностью. Вообще нету ощущения, что я читаю книжку, а потому что ощущение, что это просто какая-то инструкция, которая, кстати, очень хорошо для и работает, потому что вот иишки такие инструкции писать самое то. Есть история, когда люди постоянно обсуждают, что на русском языке слово связанность и связанность - это разные слова и так далее. Но чаще всего это всё равно относится к такому словоблудию, потому что на практике, э, когда речь идёт про непосредственну разработку, люди всё-таки не то что не мыслят этими понятиями, знание их никак не связано с тем, как пишется код. Я вот сейчас прочитав этот абзац, а, подумал вот о чём. Такое ощущение, что как будто, ну, вообще вот по Мартину, если посмотреть, а представьте, ведь почти все характеристики, которые он говорит, их можно, в общем-то, выразить в стандартных линтерах. Я уверен, что, точнее, я знаю, потому что я это видел. Существуют прямо наборы правил подлинтеры, которые так называются Clean Code rules, что-то в этом духе в разных языках, где можно, в общем-то, довольно сильно заставлять программистов следить за вот этими всеми понятиями. То есть прямо смотреть размеры классов, требовать, чтобы их было много, чтобы они были маленькие, чтобы не было большого количества переменных. Там может быть даже считать цикламатическую сложность, смотреть вот как он хотел Булиан, что переменные такие не передаются в параметры. Короче, много-много всяких интересных штук можно отслеживать. Я не видел на практике, чтобы кто-то так делал на полном серьёзе. Не в том смысле, что это совсем бесполезно. Как я уже говорил, это ивристика, когда вы уже что-то сделали и можно немножко оценивать, смотреть. Но если включить эти правила и пытаться по ним делать код, я думаю, что люди, которые это пробовали, напишите, если не так, они начинают страдать. Это тот самый случай, когда ты начинаешь понимать, что, блин, ну здесь другая ситуация, надо отключить. Например, особенно говорю, это связано с тем, что если идти к реальным примерам, когда некоторые вещи бывают довольно специфические, они сразу выпадают из всех требований, которые здесь описаны, там, начиная от автогенерации какой-нибудь, э, заканчивая тем, что просто есть области, в которых кода как бы много вроде бы, но он неразделимый. То есть вот он решает одну очень узкую задачу и очень сильно сфокусирован в одном месте. И разделять там смысла никакого нет. Вот. А, а так, конечно, с точки зрения с точки зрения здравого смысла, очевидно, что, мм, если вы какую-то логику реализуете, я очень не хочу оперировать словом класс, потому что и без классов это так работает, и эта функциональность относится к какой-то одной истории, то, естественно, она должна быть вместе. Но это почти всегда на уровне здравого смысла. А потому что, опять же, разделить заранее вы просто не сможете, потому что не понимаете, что выделять. Всегда можно чисто теоретически придумать, что и вот эту штуку можно выделить, и вот эту, и вот эту. То есть количество слоёв абстракций и элементов в вашей системе можно разбить чуть ли не до томарного уровня. Но это почти всегда есть некая граница, после которой это разделение делает только хуже, если особенно это не та часть, которая является принципиальной для вашего кода с точки зрения там гибкости. Вот поэтому вообще надо с этим быть очень-очень аккуратными. Рассмотрим реализацию стек из листинга 104. Этот класс обладает очень высокой связанностью с трёх его методов, только сайз не использует обе переменные. То есть я опять такими понятиями оперирую, не смысловыми, а просто чисто техническими, сколько кто каких переменных использует. Так, ну вникать особо не будем. Ну вот как будто бы, да, есть тег. Здесь, кстати, используется в передаче метод переменной, в которой используется декремент. По-моему, он ификсный, да, называется. Я так давно его не использовал, что я уже даже забыл название. Он префиксный или инфиксный называется. Что-то меня из головы вылетело. Ну, в общем, это вот это вот реальное зло. А у меня даже в моей жизни был один полуреквест, когда я исправлял ошибку, связанную вот с такой передачей в переменную. Там, правда, рекурсия ещё использовалась, но смысл в том, что мутация вместо того, чтобы просто передать туда новое значение, которое приводило в том случае к неправильным подсчётам, это прямо был баг. Поэтому вообще в целом их использовать, ну, желательно не использовать никогда. Но в целом, вот смотрите, допустим, он сейчас что-то будет рассказывать про то, как это поправить. Хотя давайте сейчас посмотрим, что он рассказывает. Стратеги компактных функции коротких списков параметров иногда приводит к росту переменных экземпляров, использованию подмножечных методов. Это почти всегда свидетельствует о том, что, по крайней мере, один класс пытается выделиться из более крупного класса. Постарайтесь разделить переменный метод на два и более класса, чтобы новые классы обладали более высокой связанностью. Ух, советы, конечно. Я бы так не советовал. Короче, в данном случае он показывает нам стек как класс, в котором всё связано. Но опять же акцент идёт не на то, что это стек, это единая штука, которая, ну, её не надо разделять, потому что все прекрасно знают, что такое стек, как он работает и что его часть - это не какие-то отдельные сущности, это единая структура. Поэтому опять же не надо показывать его внутренности, чтобы понимать, что стек - это некая единая штука. То же самое касается там списка односвя, ну, как чего угодно с точки зрения структур данных, потому что если мы посмотрим, нам не придёт в голову каким-то образом разделять, например, допустим, у нас в коде есть по-разному мы называем это, да, это словарь, хэшмапу, там в рубе это хэш называется где-то это ассоциативный массив называется. Ну, в общем, с точки зрения абстракции, которую мы используем, понятно, что внутри она довольно сложная. Ну, как правило, вообще она написана на другом языке, но, собственно, она является частью стандартной библиотеки, написана там где-то в кишках. Мы не всегда знаем на чём. Её при этом можно было бы написать и на нашем языке, на котором мы пишем целевом, и мы бы такие открыли сходник словаря и такие думали: "Мм, а давайте посмотрим, что здесь надо выделить или нет". Это тот самый случай, когда абстракция, она является просто цельной, она наружу никуда не протекает и живёт полностью внутри. То есть даже идея о том, что там надо смотреть какие-то переменные, как оно устроена, она, ну, неконструктивная. Какая разница? Сегодня написали так, выпустили новую версию языка, переписали. У вас этот интерфейс там никак в жизни не поменяется, если речь не идёт просто о каких-то других абстракциях. Поэтому опять же в одном случае он приводит G object, в котором миллион методов, так никто не пишет. в другом случае приводит пример, котором, если кто-то будет разделять, мы также зададимся вопросом: "А что человек попробовал в своей жизни, что вдруг ему пришла идея в голову разделять стек". Но показывать стек как пример того, что вот так писать правильно, а потом снова возвращаться в преклонную область, я считаю, что это мало поможет кому, потому что, ну, если мы берём вообще математические даже функции, там все его тезисы как бы а очень классно подтвердить любой математической функции. Будет ли это распространяться дальше? Можно ли это экстраполировать и также легко потом применять за рамками? Нет, потому что в этом случае опять возникает проблема в том, что разные люди смотрят на это по-разному, ситуации бывают разные. И, ну, нам нужно разное абстрагирование, разное разделение в зависимости от кейсов использования. И в таком случае мы получаем, опять же, если система уже хорошо разбита, прошла какие-то итерации, мы поняли, как её разделить, мы легко можем сказать: "Да, да, смотри, все эти принципы работают, вот мы их тут наложили". Но никогда в жизни такого не бывает, что хорошая система получается, потому что сначала наложили эти принципы, а потом по ним делили. А, как правило, это происходит из-за проблем с тем, что человек пытается, ну, например, ты пишешь какой-то open source, а его постоянно используют. Тут люди приходят, говорят: "Ой, а я бы хотел вот это поменять. Ой, а мне вот это неудобно". И человек, разработчик, который этим управляет, он в какой-то момент просто понимает, что вот эта часть системы слишком сильно прибита гвоздями. Тут надо принимать решение. Либо мы, например, считаем, что это нормальное поведение, которое нас устраивает, и мы с этим ничего не делаем, либо эту часть надо просто переписывать. Ну и таким образом происходит, конечно, развитие. При этом классно, когда это сопровождается каким-то фундаментальным пониманием про те же состояния, про знание, работы операционных систем, там вещи, связанные с производительностью, вещи, связаные с тем, как языки работают, с абстракциями и так далее. Много можно приводить пример разных идей и концептов, которые надо знать для того, чтобы в конечном итоге переписывание и дописывание приводили к реально прогрессу. Потому что, если нету этой базы, то я просто через это когда-то проходил, э, когда только начинал программировать, я помню, что вот были моменты часто, когда ты пишешь через месяц, тебе кажется, что весь код, который ты написал, плохой. Ты его хочешь переписать, переписываешь, но ты не делаешь это на каком-то базисе, и ты не делаешь это на основе обратной связи, которая тебя толкает в правильную сторону. Ты просто вот у тебя какие-то представления о прекрасном. Будет ли этот код лучше? Да, почти наверняка хуже. И, скорее всего, будет больше овенженеринга. Поэтому, конечно же, даже то, что я сейчас здесь говорю, можно сказать, что Кирилл, нет, всё сильно хитрее и сложней, что на самом деле правда. Для этого надо, в общем-то, учиться, учиться, ещё раз учиться. Поддержание связанности приводит к уменьшению классов. Сам акт разбиения больших функций на меньшее приводит к росту количества классов. Допустим, имеется большая функция, в которой объявлено много переменных. Сразу обращаю внимание, что здесь прямо такое сильно-сильно джавовое восприятие мира, что у вас функции, класс - это вот прям по-другому никак, потому что функция без класса быть не может. Соответственно, чем больше вы разделяете, тем больше появляется класс. Мы как-то разговаривали с ребятами на одной из конференций по поводу важности автоматизации в разных инструментах, вдшках, и они такие: "Вот я без не могу". Почему там вот мне там дата класса удобна в котлине? Почему? Потому что, говорит, я в день там создаю десятки классов, как правило, дшки, конечно, но в целом речь идёт о том, что действительно в Джаве почти каждое движение - это необходимо сделать класс, классы, классы, классы. Так далеко не везде. И это не означает то, что там какие-то совершенно другие принципы программирования, да, поэтому здесь надо быть с этим аккуратнее. Тем более, кстати, чем дальше Java развивается, тем меньше появляется необходимость этого делать. Ну, если вы вспомните котлин, там постарались, конечно, от этого уйти. Ну, сейчас я понимаю, речь не идёт о том, что там особо что-то без классов можно писать, но вообще в целом движение в сторону, что раньше нужно было там где-то анонимные классы создавать, а какие-то паттерны обязательно требовали имплементации классов, что-то на лямду ушло для обучения в начале там типа меa можно не делать, ну и так далее. Вот какие-то такие элементы, упрощающие эту всю историю, они появляются. Понятно, что глобально от классов та же самая Java никуда не уйдёт. Ну, лично я по этой причине, конечно, больше люблю Typeesриpt, при том, что мне Java нравится, на самом деле, особенно последнее, и мне приятно не писать. И свои проекты я на Джаве стараюсь писать, если это прямо вот для себя. Но Typeesриpt в этом плане там с экосистемой чуть похуже, если мы особенно ноду берём, да, чем в Джаве, но с точки зрения вот именно приятности написания кода, который тебя не заставляет таким образом фигарить, это, конечно, Typeesрипt в первую очередь. Обратите даже внимание, да, что какой он сейчас популярный, сколько на нём пишут и как мало там говорят люди про классы. Просто как пример того, что жизнь на них жизнь не строится вокруг проектирования классов. Вот. О'кей, поехали дальше. Он продолжает тут, что вы хотите один выделить небольшой фрагмент этой функции в отдельную функцию, однако выделяемый код использует четыре переменные, объявленные в исходной функции. Можете передать все четыре переменные новые функции в виде аргументов. Ни в коем случае преобразовав эти четыре переменные в переменные экземпляров классов, мы можем выделить код без передачи переменных. Таким образом, разбиние функции на меньшие фрагменты упрощается. Ну, это, кстати, опять же, я про это рассказывал в предыдущих записях о том, что он старается всё передавать без аргументов, записывая параметры внутрь класса, тем образом пряч состояние. То есть и показывал как раз примеры, когда он тестовые показывал методы, когда, грубо говоря, где-то есть скрытый стейт и вызове методов без аргументов на самом деле его меняют. Из-за этого появляется проблема правильные последовательности вызова. А что на самом деле происходит? А над чем реально работает этот метод, который он там вызывает. И мне даже в комментариях тогда написали, что Кирилл, нет, ты его неправильно понял. Он имел в виду уменьшение количества аргументов. Не то, что он их прячет. Ну, во-первых, он не это имел в виду. И потому что это вещь, которая на протяжении не только этой книги, но и вообще в целом постоянно транслирует. Это, в принципе, такой стандартный подход тех, кто прямо очень сильно завязан на класс объекты, типа прячим state максимально сильно и вместо того, чтобы какие-то штуки явно передавать, они вот спрятаны там внутри. И вот здесь он это как раз подтверждает. То есть мы не передаём аргументы постоянно, а мы всё это прячем внутрь нашего класса. И это приводит к тому, что если вы отмотаете назад, посмотрите то видео, где мы показывали тесты, что есть просто какие-то вызовы функций, ну, точнее методов в данном случае, и вообще непонятно, что происходит абсолютно, потому что где-то там какой-то скрытый реквест, с ним идёт работа, и мы даже не видим и не понимаем вообще, над чем происходит прямо сейчас работа. Вот. Ну и дальше он приходит к этой логике, что если группа функций должна работать с некоторыми переменными, не образуют ли они класс сами по себе? Конечно, образуют. Ну, а если класс утрачивают связанность, разбейте их. Я бы так сказал, что если функции работают с одними данными, то, скорее всего, речь идёт про, конечно, какую-то одну подсистему, в которой они работают, но всё-таки чуть сложнее бывает, потому что у вас может быть какая-то одна система обслуживать вторую и так далее. То есть всё бывает чуть-чуть хитрее в этом смысле. Но делать из того, что он здесь пишет, какие-то прямо практические выводы довольно сложно, кроме одного, который я много раз уже повторял. Просто не пытайтесь мыслить классами. Это примерно то же самое, как пытаться паттерны притягивать, что ой, у меня вот есть абстрактная фабрика, у меня есть мост, у меня есть там что-нибудь ещё, ну, например, там какой-то прокси. Давайте я сейчас придумаю, куда это воткнуть. Ну и, соответственно, втыкают люди потом в этом коде довольно сложно разобраться. Ну, он опять м говорит про то, что разбияние большой функции на много мелких функций также часто открывает возможность для выделения нескольких меньших классов и так далее. Ну и с его точки зрения, строение программы улучшается, а её структура становится более прозрачной. Я бы даже здесь опрос запустил на тему того, кто с этой фразой согласен, а кто нет. Мм. А дальше вообще прекраснейший пример. Для демонстрации мы воспользуемся проверенным временем примером из замечательной книги Кнута Literate Programming. В листинге 105 представлена программа Кнута Print Primes, переведённая на Java. Справедливостью стоит отметить, что это не та программа, которую написал Кнут, а та, которую выводит утилита web. Что-то там вот где-то делается, я воспользуюсь ей, потому что она является отличной отправной точечкой для разбиения большой функции на несколько меньше функций классов. Ребята, прекраснейший пример. Прямо сейчас будет классно его обсудить, и я тут скажу много всего. Короче, если вы не смотрите этот подкаст, а просто слушаете и как мы обычно говорим, моете посуду, он показывает здесь класс. Ну, это просто статический метод. То есть, по сути, как такового класса здесь нет, да? То есть мы вообще не говорим про класс. Это просто некая вот алгоритм тут реализованный, в котором только переменных на экран. А дальше, собственно, идёт у нас цикл, который получает вот, честно говоря, с ходу вот так глядя, сказать, что я однозначно понимаю всё, что здесь происходит, немножко проблемно, потому что действительно сам по себе просто цикл он ещё и вложенный, да, тут сложно, но судя по тому, что я вижу, как будто тут с одной стороны одновременно вычисляется и простые числа, и одновременно формируется square ord m Короче, такое ощущение, что как будто одновременно табличка формируется, которую он там собирает потом распечатывать. Возможно, я не прав, но это выглядит так, честно говоря, да. И после этого он, собственно, идёт по ней циклам и выводит в каком-то формате там ещё какие-то у него смещения. Тра-та-та. В общем, короче, он это всё распечатывает просто тупо на экран в терминал и говорит, что вот опять же напомню, что всё это речь идёт про мы находимся в разделе про классы. Теперь смотрите, речь идёт о том, что простая программа, которая, по идее, считает простые числа и печатает на экран. Вот если даже туда не смотреть, вот просто с точки зрения здравого смысла, мы, когда смотрим на эту программу, нам очевидно, понятно, что она просто плохо написана. Почему? Ну, во-первых, вычисление простых чисел - это некая самостоятельная операция, очевидная, математическая, которая должна быть извлечена. То есть, ну, ни при каких обстоятельствах не может быть ситуации, что мы будем вычислять простые числа прямо в процессе того, как мы печатаем на экран. То есть это вообще просто базис того, что из себя представляет функция как смысл, да, что она какая-то абстракция. То есть вычисление простых чисел - это отдельная штука. И ясен красен, что мы ни о каких классах не говорим. Мы должны выносить эту логику отдельно. Причём не просто отдельно. Мы понимаем, что когда речь идёт про то, чтобы печатать простые числа, нам нужна не просто какая-то штука, которая может их, например, то есть есть несколько вариантов. Может быть, например, мм в нашем, а, языке есть функция, которая там их генерирует, а может быть какая-то библиотечка. Но, допустим, мы решили сами написать. О'кей, мы напишем какую-нибудь функцию, которая генерирует то количество простых чисел, которое мы хотим. Можем сделать так, можем сделать так. О'кей. Есть альтернатива, опять же, которая тоже довольно понятна тем, кто, ну, в программировании разбирается, что есть такая штука, как генераторы. То есть мы фактически создаём генератор, который нам возвращает простые числа. И таким образом вычисление идёт лениво. То есть вот сколько мы его попросим, он постепенно эти числа выдаёт. И дальше мы можем это где-то использовать. Соответственно, как только мы это написали, и тут уже неважно, класс - это, если это Java, потому что, ну, допустим, нам придётся в класс, это просто функция, которая возвращает этот генератор, а, и так далее. Это всё очень вторично и зависит от специфики конкретного языка. Соответственно, в Джаве, скорее всего, это действительно будет класс. Мы ставим объект у этого объекта просим next, ну, если так упрощённо, как бы свой ручной генератор. А этот next возвращаю в следующий элемент. Ну, и таким образом мы можем использовать его там в циклах или ещё где-то, как хотим. В других языках это может быть прямо конкретная конструкция, генератор, которая вообще не связана там с созданием объекта и класса или класс, если опять же захотим, потому что в разных языках, то есть там есть генераторы, как бы в этом плане можно имитировать через создание класса и объектов на базе него, внутри которых мы лениво вычисления эти производим. А может быть, у нас есть конструкция, которая позволяет это сразу сделать. Но в любом случае, очевидным образом это вообще никак не связано с печатью. Это некоторая самостоятельная штука, которая существует в вакууме. То есть в любом случае мы её выделили, и у нас просто после этого тут же количество кода просто сокращается в половину, потому что вот эта логика, она просто уходит. Дальше, соответственно, есть просто логика того, чтобы построить эту табличку. Ну, о'кей, она тут написана, она, в принципе, довольно компактная. Вот мы строим эту табличку и после этого мм печать. Но тут, понимаете, какая возникает опять проблема того, какие примеры он выбирает. Он выбирает пример, который никакого отношения к реальной жизни не имеет. Потому что в каком состоянии, где нам понадобится печатать каким-то хитрым образом простые числа на экран. Допустим, вы скажете: "Ну вот Кирилл, представь, там есть такие интерфейсы tui называются, да? Это терминал UI. Мы пишем что-то такое современное, где реально надо классно всё это выводить". Но тогда я возражу, если мы действительно так делаем, какого чёрта мы в принципе самостоятельно этим управляем? Потому что, если там перерисовка экрана, если там, а, всякие индикаторы и так далее, ну, много всякого разного добра, ещё это всё красиво происходит, то мы вообще, в принципе, не должны никакими принтами это делать. Потому что если мы берём современную вообще историю, тогда, кстати, тоже было, просто, наверное, далеко не во всех языках, но опять же об этом точно люди понимали и знали. А что в каждом языке есть библиотечки для TUI, для того, чтобы в терминале писать юайку. Я думаю, что вы прекрасно знаете, насколько сейчас это популярно и распространено, и как много таких библиотек идёт, и как часто с ними работают. Почему? По одной простой причине. А агенты, всякие кодексы, cloudди код, копайлоты, там, OpenCд и другие, они прямо активно подстегнули всю эту штуку. Она и до этого, в принципе, активная была, да, но они её очень сильно подстегнули, и под них прямо специально эти библиотеки разрабатывались и развиваются они очень классно и круто. И, соответственно, вот эта вся история с выводом, которая есть, она просто не будет руками делаться. Мы берём эту штуку и, соответственно, выводим. Но даже если, допустим, он решил по какой-то причине не использовать, то история про то, чтобы это как-то сделать лучше, действительно тогда нужно думать не в терминах а нашей конкретной программы. Если уж он хочет делать классно, как он это показывает нам, то мы должны тогда действительно сразу понять, что ага, мы хотим как-то красиво это выводить. Соответственно, значит, это у нас должна быть какая-то либо для рисования, потому что, например, ну, если мы в вебе это показывали, представьте, если мы в HTML это просто выводили, соответственно, у нас был довольно, ну, деревянный код для того, чтобы собрать эту hтtмэльку. Это просто бы какая-то конкатенация была элементов. Соответственно, мы такие: "Ага, значит, нужно какая-то нужна какая-то абстракция". И тут мы приходим к тому, что для того, чтобы реализовывать какую-то программу, допустим, это какой-то бизнес, который от нас что-то хочет, да, и мы должны сделать, мы такие сидим: "Ага, давайте вместо того, чтобы заниматься бизнес-задачами, мы сейчас будем свою UI библиотеку пилить". Опять же, это работает в определённых кейсах. Возьмём тех же самых ребят, которые сейчас агентов пилят, да, они это делают и да, они принимают активное участие в создании подобных библиотек, но это явно не та ситуация. Короче, если мы начинаем раскладывать эту ситуацию со всех реальных возможных кейсов, которые вот могут быть, то всегда оказывается, что должен быть какой-то другой путь. То есть, если мы уж за это взялись, то тогда давайте уж делать termрмиal UI библиотеку. Если это нам не надо, то тогда, наверное, надо взять готовую. Если вообще всё это не надо, то зачем, в принципе, заморачиваться? Просто вывели эти простые числа на экран. Короче, есть вопросики. И да, можно сказать, ну, он просто вот пример хотел показать, чтобы ничего реального не было. Но в этом и проблема, что вся книга на насквозь такая, то есть ничего реального нет. И нацепить это очень сложно из-за этого проверить правильность, очень сложно применить к себе и действительно оценить, а надо это делать или нет. Ну как вы понимаете, я со своего опыта наоборот оцениваю это скорее негативно. Ну кто-то скажет: "Нет, это нормально. Вот здесь мы и разойдёмся с такими людьми". А, но я так на всё это смотрю. И опять же, как человек, создавший огромное количество курсов за последние там, ну, много лет, больше десяти уже, правильная работа с аналогиями, показывание примеров, которые приближают человека к реальной жизни - это очень важный аспект, который я сам постоянно пропагандирую, постоянно использую. То есть меня, например, всегда, знаете, какая штука напрягала. Я понимаю, почему так делают, но она часто приводит к последствиям, когда показывают доку каких-то фреймворков или примера кода, как чтобы показать какой-то акцент, а типа пишите так. Всё остальное при этом обычно делают через одно место. Во-первых, используют какие-то фейковые данные непонятные, сильно упрощают и так далее. И вот почти всегда в этих моментах есть мм уровень упрощения, который допускать нельзя, потому что если он присутствует, то это приводит к тому, что люди как бы люди всегда копируют, и они видят, когда что не только пример, который им показали, но и код окружающий вот написан определённым образом, они потом начинают копировать и писать, типа, ну нам же так показали. А потом, когда ты разговариваешь с людьми, естественно, любой автор фреймворка или библиотеки скажет: "Ребята, ну я просто вот там показываю, потому что, ну мне надо уместить это всё на полэкрана". Потому что, если я сейчас начну использовать фреймворк или показывать какие-то дополнительные фишки, чтобы это всё красиво выглядело, то оно будет стрёмно. И они тоже правы по-своему. Поэтому здесь как бы правда где-то посередине она, конечно, тоже с опытом приходит, когда ты много учишь людей, ты понимаешь, что вот такие вещи можно делать допустимо, а вот такие вещи всё-таки это уже перебор, потому что они, ну, неправильно впечатление создадут, и люди потом будут думать, что это нормально. К сожалению, здесь нет простого способа. Я не могу вам сказать, что существуют фреймворк, паттерны или вот как типа разбивай классы на более мелкие, чтобы определить такую штуку. Я к этому шёл очень много лет перед тем, как научился правильно такие вещи описывать. И то под правильно я не имею в виду виду, что я с первого раза там сделал классно. А просто есть вопрос, который умею себе задавать. Есть опыт, есть рефлексия и, соответственно, изменения в процессе, когда там, например, студенты говорят, что ой, вот тут вот понятно, вот тут нет. Это тоже целое искусство. Но дальше то, что он делает, меня, конечно, удивило. Опять же, потому что я давно эти блоки не читал. Вот буквально перед записью это посмотрел, и получилось интересно, что он сделал. Он код, который, в принципе, к классам никакого отношения не имеет, потому что, объясню ещё, кстати, почему. Потому что здесь как такового состояния нет. Когда мы вообще обсуждаем объекты и классы как и объектноориентированное программирование, то речь идёт, как правило, об объектах, у которых есть состояние, у которых есть время жизни. Ну, то есть там что-то живое, что не просто вычисление, которое закончилось выводом на экран. И классы здесь, честно говоря, это очень притянутая вещь, если уж так глобально смотреть. Но, допустим, что он начинает делать? Он вводит кучу классов и кучу элементов, которые приводят к тому, что кода становится, ну, я не совру, в три, в пять раз больше. С причём довольно хитрого кода, в котором разобраться уже вообще большая проблема. Значит, начнём по порядку. Что ему не нравится? многоуровняя вложность, множество странных переменных, структура жёсткая привязка. По крайней мере, одну большую функцию следует разбить на несколько меньше функций. То есть обратите внимание, вот он не мыслится с точки зрения того, что так вот у вас есть вычисление простых чисел, его надо отделить. Он чисто вот формально подходит тому, что так, нам надо разбивать. Дальше он делает прайм принте. Класс. Почему прайм принтер? А какая разница, что он печатает? Ну то есть это же просто должен быть принтер, а ему на вход передаются какие-то числа. Вот оно как бы граница абстракции, разделения. Нет, но у него это называется прямо прайм принтер. Ну то есть получается, что вся эта программа, она вся целиком со всеми её элементами конкретно под печать простых мм чисел. Но тогда получается, что она слишком жёсткая. То есть она слишком жёстко пришита к этому, и она всё равно является негибкой. То есть ни класса туда не вносят, ничего, что позволило бы хоть что-то там менять и как-то переиспользовать. Ну и вообще как-то с этим работать. То есть это просто некая такая компактная одноразовая штука. В общем, он создаёт здесь в мейне кучу переменных. Вот количество простых чисел, 100 штук. Тут же он передаёт количество строк на страничку. И мне, кстати, сразу тогда хочется сказать, что, ну, а как же, мы же говорили про единую ответственность, да? А у нас тут вычисление простых чисел. И причём здесь вообще какие-то row per page? Это же вообще другая ответственность. То есть это, во-первых, печать, во-вторых, мы тут вообще задаём какие-то визуальные вещи. И где как бы простые числа, а где визуальные вещи, да? Короче, он создаёт этот принтер как объект. Опять же, в чём здесь объект? Потому что нету состояния, мы просто печатаем, выше это было видно. Ну, допустим, создаёт. Но по факту, кстати, это используется такой паттерн. Я про него тоже писал, как-то его редко выделяют, но он классический такой, частый паттерн, когда объект создаётся только для конфигурации. То есть, по сути, состояния внутри никакого нет. Мы передаём туда просто параметр, чтобы его сконфигурировать. Классный пример - это тот же самый Markдаун. А его часто так делают. То есть, например, там New MarkDO передали параметры, а потом рендер в разных местах с передачей туда соответственно непосредственно данных. Так, поехали дальше. Короче, у нас получается штука, которая в себе объединяет и печать, и работу с простыми числами. У него есть primeйм, который генереate. Мы передаём количество простых чисел. Опять же, при этом, заметьте, статики много. То есть тот же самый генереate, очевидно, ставить, потому что это просто функция. Но при этом он возвращает сразу набор готовых простых чисел. То есть он не возвращает генератор, соответственно, ну, там идёт какое-то вычисление. Вот, кстати, тоже интересно, потому что логика какая, он говорит: "О'кей, надо сделать мелкие функции, а потом бам, внезапно праймгенерейтор оказался в одном месте, а печать оказалась в другом месте". Как будто это вторично по отношению к объёму и как будто это естественным образом получается именно такое разделение. Ну, учитывая, что он делает акцент именно на размере. А в реальности это мы говорим про ответственности, да, что неответственности, господи, я уже сам начал это слово использовать, я его не люблю. Именно по той причине, что оно не отражает действительность часто. Мы просто понимаем как бы граница абстракции, что у нас есть простые числа, да, и печать. Ну и смотрите, что получается. Вот даже если на эту печать посмотреть, у него тут table printer, row col page printer. Эта штука суперспецифичная. Я не понимаю, как она может быть переиспользована в этом смысле, потому что если даже посмотреть на то, как печатаются штуки в консоли, очень часто там, ну, во-первых, это просто по-другому работает, то есть не так это не делают. Во-вторых, э банально посмотрите на JavaScript. У них там в консоли есть метод, который называется conso table, который просто берёт и в табличном виде выводит какие-то данные. Вот. И дальше, если мы заходим в этот класс, который отвечает за печать, вот этот pageйпнтер, мы получаем класс, которым размером такой же или даже больше, чем исходный код, нет, больше, чем исходный код, с кучей приватных методов, который делает кучу каких-то вычислений. Этот же класс всё это ещё печатает. Естественно, вся эта печать никак не связана там с с какой-то логикой, которая в UI в терминальном может вообще возникать. То есть получается, что, ну, если это развивать, это потом вообще невозможно с этим работать. Ну и, соответственно, тут просто печать, печать, печать, куча каких-то переменных внутри. Ну, понятно каких. То есть он пытается там табличку делать, вычисление, но смысл в том, что раньше было там одним простым циклом превратилось в класс с состоянием и с кучей данных. То есть очень много переменных. Кстати, интересно вот насколько здесь внутри мм эта штука будет работать, если, допустим, я передам в принт другой набор элементов, а распечатает ли он их мне нормально? То есть я имею в виду вот эти вот параметры, которые он передал, не сломаются ли, потому что состояние там остаётся и вдруг оно меняется в процессе. Ну вот я вот сейчас смотрю page number. Вот page number тут записан или нет? Page number как будто бы не записан. Ну, скажем так, разбираться в этом коде, это надо прямо сидеть и разбираться. Тут и циклы, и вычисления, что там последнее, не последнее, и печати. Причём печати идут ещё и в разных местах. То есть мы не только в одном месте печатаем, оно разнесено по разным методам. А что, кстати, будет очень сильно усложнять печать. Вот он тут использует ещё стримы. Set output, а, то есть ещё снаружи это можно вообще передать и поправить. Ну и после этого мы переходим к primenator. Здесь просто опять же класс, который там с кучей статики, то есть там всё статика, который, собственно, генерирует всё это добро. Куча больших переменных с длинными названиями и кода здесь, ну, в раза в два минимум больше, чем было в исходном варианте. Всё это выглядит очень страшно. То есть я не могу себе представить, что в своём а коде я у себя сделаю такой класс primeе, который вот так вот мне будет генерировать простые числа. Выглядит довольно овенженерно. То есть я представляю себе, что я могу написать функцию создания простого числа. Возможно, там будет порядковый номер этого числа, что-нибудь в этом духе. То есть, короче, смысл в том, что когда мы говорим primeйменеator и эта штука генерит намного простых чисел, это вообще штука тогда по идее должна быть не самой по себе сущностью, а должна быть функция, которая, например, допустим, умеет генерить простые числа и другая какая-то штука, которая из них может сгенерировать набор. Причём простое число можно, например, генерировать там на базе там переданного предыдущего или как по количеству какое-то простое число и так далее. То есть можно много всяких разных комбинаций придумать, при которых это будет выглядеть лучше с точки зрения того, как мы комбинаторно это соединяем. Потому что праймнееatorр очень странная штука, да, с точки зрения того, что это некая единая сущность. Вот. Ну, как мы уже говорили, что генератор - это отдельная история. В общем, я такой код бы не писал, не написал бы, не одобрил бы и считаю, что нет ни одной причины, почему вообще такой код может существовать и почему мы можем рассматривать такой код как, ну, вообще как нормальный код. А он дальше начинает оправдываться. Прежде всего бросается в глаза, что программа стала значительно длиннее. От одной с небольшой страниц она расслась почти до трёх страниц, на самом деле больше. Это объясняется несколько причины. Во-первых, переработанные программы используются более длинные и более содержательные на переменных. Только он забыл сказать о том, что содержательные имена переменных можно было сделать и в исходной программе, не увеличивая её размера. А в этой программе количество переменных стало просто во много раз больше, потому что их появилось намного больше, появилось намного больше состояний и стало намного бо сложнее всем этим управлять. Дальше он пишет: "В-вторых, объявление функций в классов переработанной версии используется для комментирования кода, не засчитывается, там просто тупо много кода". В-третьих, пробелы, дополнительное форматирование обеспечивает убочитаемость программы. Опять же, это можно было сделать в исходном варианте, но там очевидно просто, что в одно место воткнули вообще всё вычисление. Обратите внимание на логическое разбиение программы в соответствии с тремя основными видами деятельности. Основной код программы содержится в классе Prime Printer. Он отвечает за управление средой выполнения. И Netcд изменится в случае смены механизма вызова. Ну и так далее. Короче, он там уже говорит, что если программа будет преобразована служба SOAP, то изменения будут внесены в код прайм Printer. Вот это вообще очень странно, да, что мы эту штуку будем преобразовывать в службу SUAP. Ну и дальше он начинает объяснять, почему всё это правильно, почему это классно, почему всё сработает, как надо. В принципе, я уже много сказал, насколько получившийся результат - это адовый овенжениринг. Даже те люди, которые следуют его идеям, я уверен, не стали бы писать такую сложную штуку для такой простой задачи. Тем более, опять же, задача абсолютно искусственная. И понятно, что если человек сидит и вот этим занимается, там видно, что такой код не напишешь за одну секунду, если ты не пользуешься там агентом каким-нибудь шным, то даже сама мысль о том, что ты сидишь и часами пишешь этот код, должна настораживать, потому что это явно не имеет никакого отношения к бизнесовой части. Хотя при том, что мы сейчас вроде бы не про бизнес, а вообще про архитектуру и в целом, как писать, но даже тут можно, мне кажется, почувствовать очень легко, насколько это просто потеря времени и бессмысленность. делая вот эту штуку. Ну а дальше он начинает объяснять, что там и тестам бы помогло, и так далее, и так далее. Но я вот совсем с ним не согласен, в том числе, потому что класс, который там всё подряд печатает на каждом этапе, в каждой своей функции практически, это как раз вот гораздо большая проблема, которая в итоге появилась и её не было. А потому что у него, в общем-то, сломалась концепция управления состоянием, да? А появились побочные эффекты. В итоге тестировать-то как раз такое гораздо сложнее. Тем более, смотрите, оно же всё синхронно считается. То есть можно было просто банально, знаете, как сделать? Вот если мы говорим о том, что это не стриминг, а простые числа появились сразу, мы сразу всё печатаем на экран. Даже если бы он банально вернул просто строчку, которая, ну, вот у нас сформирована получилась табличка, которую мы хотим распечатать, и печать мы делаем именно в базовой программе, то есть в вовне, то как минимум здесь уже было бы лучше, потому что это можно легко в тестах посмотреть, распечатать, посмотреть, какой в итоге результат. Мы можем даже какой-то сNпшот тестирования сделать, сравнить с результатом, мм, чем то, что сделал он в конечном итоге, ну, фактически сделав в класс нетестируемым вообще. Поехали дальше. Большинство систем находится в процессе непрерывных изменений. Каждое изменение создаёт риск того, что остальные части систем будут работать не так, как мы ожидаем. А в чистой системе класса организован таким образом, чтобы риск от изменений был сведён к минимуму. Класс SQL в листинге 109 используется для построения правильно сформированных строк SQL по соответствующим метаданным. А работа ещё не завершена, поэтому класс не поддержит много функций SQL. Ну о'кей. Когда придёт время включения в класс SQL поддержки Update, придётся открыть этот класс для внесения изменений. Но, как уже говорилось, открытие класса стоит риск, любые изменения стоют потенциальные возможность для решения работы там, и так далее, и так далее. Смотрим название и видим, что, ну, здесь, короче, много всего намешано, на самом деле. Здесь намешана и работа просто с табличкой, допустим, там, Create, insert и так далее. Здесь есть выборки, причём не просто выборки, но и выборки, связанные там с критериями. То есть мы какую-то фильтрацию проводим и так, ну, и всё в этом духе. Вот что он дальше пишет. Класс SQL изменяется при добавлении нового типа команды. Кроме того, он будет изменяться при изменении подробности реализации уже существующего типа. Скажем, если нам понадобится изменить функциональ селект для поддержки подчинённой выборки. Две причины для изменения означают, что класс SQL нарушает принципы единой ответственности. Ну, он говорит, что здесь много всего, но это правда. То есть вот люди, которые работали с ормками, они сразу здесь увидят, что это довольно странная имплементация. Хотя это первый раз, когда вот он показал пример, про который можно сказать, что, ну, действительно, вот если бы мы с нуля что-то такое писали, но вполне возможно у нас бы такое получилось. То есть мы делаем, грубо говоря, абстракцию над табличкой, которая, собственно, делает вставки, апдейты и селекты. Но если вот так вот абстрагироваться от того, что мы знаем, как устроены, у Рэмки и как там всё разбито, опять же, можем ли мы прийти к этому? Вот если мы этот класс дадим, допустим, человеку, возьмём какого-нибудь дата-аналитика, да, который, в принципе, скелем работает и который, например, знает Python, а мы дадим ему такой же класс в Пайthне, и при этом он никогда не работал с РМ и не знает, как обычно такие вещи разбиваются. Скажем, вот такой-то принцип, давай попробую разбей. Я думаю, даже вот с ходу, когда он на это смотрит, он даже не поймёт, а в чём, собственно, ну ладно, о'кей, я могу согласиться там с тем, что, ну, как будто бы действительно здесь много всего, но вот проблема разбиения, она тоже не очень понятна по какому принципу, что куда. Это не очень очевидно и можно сильно по-разному, потому что в конечном итоге выясняется, что здесь дело не только в разных классах, а в том, что, например, управление там ставкой и изменениями - это, допустим, вообще одна система, потом выборки - это там вторая система, а тут у нас есть репозитории, вот, а есть вообще паттерны, потому что они просто на опыте появились, и люди знают, как это надо делать и так далее. При этом, как правило, если опять же берём какие-то навороченные системы, там вообще вот так вот апдейты и инсерты не делаются, то есть там всё сильно хитрее. И к таким вещам шли, ну, долго и устроены они очень сложно. А можем ли мы сделать вывод, что нам надо сразу идти туда? Потому что это же правильно, это же классно. Там же, если какой-нибудь кибернейт открыть, там классов сколько? Тысячи, наверное, да, чтобы вот это всё реализовать, потому что у нас сегодня одна табличка, завтра две, между ними связи и так далее. И в итоге получается, что а где та точка, где надо остановиться? То есть почему мы вообще решили в принципе вот это всё делать? Да, возникает вопрос. Тем более, кстати, как правило, если опять же вспомнить, как делают все современные библиотечки, которые уже говорят, что нет, - это всё перебор, надо по-другому, так там вообще просто query builder. А query builder он довольно простой, на самом деле, по своей сути. И если это какие-то динамические языки, там вообще классов может не быть, но самое главное, что это просто, как правило, цепочка. Это билдер, ну, паттерн билдер, он не просто так называется стильдер, который, собственно, собирает в итоге запрос, который уже можно делать через какую-то другую фигню, которая непосредственно выполняет запрос. То есть сам билдер - это просто штука, которая абстракция над СQэлем для того, чтобы в удобной форме, не работая со строчками, собрать запрос. Вот как бы прямая, понятная, отдельная абстракция. Если её объяснить, она станет понятна. А, но она появляется не потому, что мы мыслим на классах, она появляется потому, что есть проблематика сбора сложных запросов, особенно с условиями, чтобы не пользоваться строчками, желательно ещё типизацию. И таким образом мы понимаем, что эта штука может быть отдельным, пожалуйста, если хотите, классом, да, в котором это имплементируем. Ну и дальше уже в рамках нашей системы, если нам нужны ещё вставки, апдейты и так далее, мы понимаем, куда её подключить. Но при этом сам билдер, который непосредственно собирает, очевидным образом, для этого не надо думать на уровне классов, не должен заниматься там вставками из базы. То есть эта штука просто собирает SQL, заменяя именно логику формирования строчки. Ну дальше, э скажу так, он начинает разбивать, разбивать опять же по принципу: почему бы не сделать так? То есть он там по каким-то своим внутренним принципам сделал, и, наверное, сложно с этим спорить без опыта, потому что не всегда понятно. То есть выглядит круче, потому что, ну, смотрите, он же разбил. А вот действительно так это или не так, большой вопрос. Ну, а по факту это не так. То есть, если посмотреть, он сделал, а, абстрактный класс SCL, в котором есть generate, а дальше от него create escale, select escale, и они его наследуют. Я не очень понимаю эту логику, но я точно знаю, что это делается не так. Опять же, потому что в том числе у меня не только есть там опыт работы с ормами, понятно, большим количеством разных, но и я сам писал орэмку и достаточно неплохо разбираюсь в том, как они устроены. Так вот, так это не делают. То есть это не история про то, что мы делим по принципу: вот это Create SQL, это Select SQL, оно на других принципах основано. Ну, ИРТ, соответственно, и дальше он пошёл, он фактически просто взял и каждый метод, который там есть, преобразовал свой собственный класс. Ну и, конечно же, получилось в 100 раз больше кода. Но только вопрос даже не в этом, а в том, как мы потом с этим работать будем. Ну а его главный поинт, что каждый код каждого класса становится до смешного простым, и время, необходимое для понимания класса, падает почти до нуля. Вероятность того, что одна из функций нарушит работа другой, ничтожно мала с точки зрения тестирования, проверка фрагментов логики в этом решении упрощается, поскольку все классы изолированы друг от друга. Не могу не сказать о том, что мне очень нравится концепция, которую когда-то описал Фредерик Брукс по поводу серебряной пули и случайной и необходимой сложности. Случайная сложность возникает, когда мы используем неправильные инструменты или не там, или не так. А вот необходимая сложность, она возникает вследствие задачи, её убрать невозможно. Мы можем убрать случайную сложность, но необходимую сложность мы не уберём. То есть это непосредственно логика нашей программы. Если у нас есть, например, 10 кейсов, которые мы обязаны запрограммировать, они ведут себя по-разному, мы напишем всё равно код для 10ти кейсов, каким бы он маленьким не был, это всё будет 10 разных кейсов. Здесь примерно то же самое. Он как бы разбил и такой: "Ну, смотрите, вот маленький классик. Прикольно, да". Но я не очень понимаю, почему он это сравнивает, потому что, ну, в конечном итоге-то мы делаем целую систему. И если раньше, например, я опять же не говорю, что это правильно, потому что понятное дело, вот этот класс изначально, который он написал, но это довольно наивная имплементация взаимодействия с базой данных, хотя вполне реально, если у нас вообще ничего нет и мы вот для себя хоть что-то решили написать, о'кей, в принципе, с этим жить можно, но когда он это разбил, оно же всё равно должно быть соединеное. У вас всё равно все эти операции есть, и всё равно в конечном итоге вы делаете систему, в которой все эти элементы должны быть связаны. То есть у вас ещё помимо прочего появится клей, дополнительный код, который соединяет все эти классы между собой. А, и почему он вот эту всю систему не сравнивает с тем, что было изначально? Вот это загадка. Но опять же повторюсь, это, я считаю, что с его колоколь - это манипуляция, которая позволяет вот так вот как бы уйти от ответа, который ему никто не ответит, потому что книжка-то уже написана. Ну и дальше он пишет, насколько это круто, переработанная логика SK положительно во всех отношениях. Она поддерживает принцип единой ответственности. Она также поддерживает другой ключевой принцип проектирования, называемые принцип открытости, закрытости. Классы должны быть открыты для расширений, но закрыты для модификации. Ну, я так понимаю, что в данном случае он это говорит о том, что, смотрите, мы же наследуемся и, соответственно, мы можем базовый класс не менять, но у тебя в базовом классе-то ничего не осталось. У тебя всё осталось в этих расширениях, которые, если тебе нужно будет менять, ты откроешь их и поменяешь. Ты дальше наследоваться не будешь. Поэтому те же яйца только в профиль. И в принципе всё. Дальше он говорит, как будто всё это хорошо. Другой вопрос, я бы хотел, говорю, посмотреть, как он будет создавать объекты этих классов, чтобы потом этим манипулировать, что если ему надо сделать апдейт, он будет делать new update SQL. Если ему надо делать выборку New Find SQL, это всё выглядит крайне странно. Так, конечно, не пишут, но здесь это остаётся за рамками книги. Он об этом ничего не пишет. Так, изоляция изменений. Потребности меняются со временем. Следовательно, меняется код. А в начальном курсе объектно ориентированного программирования мы узнали, что классы делятся на конкретные содержащие подробности реализации, абстрактные и представляющие только концепции. Если клиентский класс зависит от конкретных потребностей, то изменения их подробностей может нарушить его работоспособность. Чтобы изолировать воздействие этих подробностей на класс система вводятся интерфейсы, абстрактные классы. Да, интерфейсы вводятся. Это, кстати, больше имеет отношение вообще к типам, честно так говоря, а не к оп, но опять же оставим это за скобками, потому что О и типы так сильно переплетены в таких мейнстримовых объекторитированных языках, что люди практически никогда это не разделяют и не видят разницу. А на самом деле она есть. И другие языки это очень хорошо подчёркивают, когда речь идёт на самом деле про типы, но притягивают к этому оп, потому что интерфейсы не являются прерогативой классовых языков. И они прекрасно есть везде. И всё с ними хорошо. И всё хорошо с абстракциями в других языках тоже. Вот. Но при этом, видите, он выше говорит про то, что мы про учимся ООП. Значит, зависимость от конкретики создаёт проблемы при тестировании системы. Если мы строим класс портфолио, завися от внешнего API Tokyo Stock Exchange, резкий вход. А я ещё не отошёл от SQL, но мы уже говорим про вещи, которые не обсуждались заранее, поэтому, видимо, опять придётся по контексту понимать, о чём идёт речь. Для вычисления текущей стоимости портфеля ценных бумаг наши тестовые сценарии начинают зависеть от ненадёжного внешнего фактора. Трудно написать тест, если вы получите разные ответы каждые 5 минут. Вместо того, чтобы проектировать портфолио с прямой зависимостью Stock Exchange, мы ставим интерфейс Stock Exchange, в котором объявляем один метод. Я скажу так, вот честно, я небольшой специалист по стокам, обменам и по портфолио, хотя эти слова знаю, знаю, что они связаны. Поэтому мне очень сложно будет комментировать это именно с точки зрения смыслов, потому что если опять же идти на тот вариант осмысления происходящего через его подход, я, наверное, смогу про это поговорить и сказать, что смотрите, там же вот маленькая функция, большая функция и так далее. Но это всё не имеет никакого смысла, если мы не понимаем, а в данном случае я не понимаю, понятно, что те, кто смотрит, очень многие 100% хорошо это понимают. А, собственно, о чём идёт речь, какую часть системы мы строим, что здесь должно происходить. Это может быть вычисление, это вывод, это база, это может быть сервис, может это элемент сервиса, может это се, может быть это ещё что-то, ну, именно сервисный класс внутри какого-то сервиса, какая модель, какие связаны между собой данные и вообще какие юзкейсы у нас тут есть. Без всего этого вообще невозможно сказать о том, куда мы идём, нормально ли всё это. Причём очень часто ведь ещё в чём оторвано здесь? Очень многие классы показывают так, как будто они существуют в вакууме. А на самом деле это всё хранится в памяти, это хранится в базе, это передаётся по сети и так далее. Поэтому здесь сложно очень говорить о том, насколько то, что здесь написано, вообще адекватно. Поэтому вот эту штуку оставлю на домашнее задание, если вы сами почитаете, посмотрите, попробуйте разобраться в том, что он говорит. Я, наверное, закончу, потому что, в принципе, дальше глава заканчивается, и после этого он, несмотря на то, что как будто бы рассказывает про системы, честно говоря, он уходит уже в какие-то либо паттерны, либо начинает Java притягивать. Дальше многопоточность и разбор чисто джавовых штук, которые нам уже просто будут не очень интересны. В общем, ребят, большое спасибо, если дослушали до конца. Понимаю, что вышло немножко сумбурно, но мне очень хотелось закончить этот цикл. Теперь можно на него всегда ссылаться. Он в отдельном плейлисте лежит. Я надеюсь, вам понравилось. Я надеюсь, вы узнали что-то новое, потому что, честно говоря, наша главная была история не в том, чтобы сказать, какой, как там всё плохо в этой книжке, а чтобы узнать что-то классное и новое, потому что я именно делился своим опытом, рассказываю про эту книжку, бер её как пример. Мне, кстати, это гораздо больше нравится, чем просто сидеть и рассказывать, как правильно классно делать это на примере. Просто в данном случае пример был такой как бы на противопоставлении. В другом случае это будут другие примеры, где я буду, а, говорить про более глубокие вещи, но уже на более правильных примерах. с моей точки зрения, потому что, ну, вот у меня такой vision, а, по тому, что мы делаем. Всем большое спасибо, кто дослушал. Оставляйте свои, пожалуйста, комментарии, если нравится. Ставьте лайки, ставьте дизлайки, если не понравилось. Пишите, почему. Я всегда читаю всё, что вы пишете, потому что где я неправильно что понял, киваю и продолжаю дальше делать своё дело. Всем спасибо. Пока.