company_banner

Прощай, чистый код

Автор оригинала: Dan Abramov
  • Перевод


Был поздний вечер.

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

Код работал.

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

Код, о котором идёт речь, выглядел примерно так:

let Rectangle = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
};

let Oval = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
  resizeTop(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
  resizeBottom(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
};

let Header = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },  
}

let TextBlock = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 10 однотипных строк вычислений
  },
};

Меня все эти вычисления, все эти почти одинаковые строки, сильно зацепили.

Код не был чистым.

Основной объём однотипных строк наблюдался в тех местах, где задавалось перемещение фигур в одном и том же направлении. Например, в методе Oval.resizeLeft() был код, похожий на тот, который можно было найти в Header.resizeLeft(). Дело было в том, что оба эти метода отвечают за изменения фигуры, выполняемые при перемещении маркера влево.

Похожими были и методы фигур, имеющих одну и ту же форму. Например, Oval.resizeLeft() был похож на все остальные методы фигуры Oval. Все эти методы работали с овалами — отсюда и их сходство. Дублирующийся код можно было найти в объектах Rectangle, Header и TextBlock, так как текстовые блоки представляли собой прямоугольники.

У меня появилась идея.

Можно избавиться от дублирующихся конструкций, по-другому сгруппировав код. Например — так:

let Directions = {
  top(...) {
    // 5 уникальных строк вычислений
  },
  left(...) {
    // 5 уникальных строк вычислений
  },
  bottom(...) {
    // 5 уникальных строк вычислений
  },
  right(...) {
    // 5 уникальных строк вычислений
  },
};

let Shapes = {
  Oval(...) {
    // 5 уникальных строк вычислений
  },
  Rectangle(...) {
    // 5 уникальных строк вычислений
  },
}

Затем можно прибегнуть к композиции и собрать из этих базовых методов то, что нужно:

let {top, bottom, left, right} = Directions;

function createHandle(directions) {
  // 20 строк кода
}

let fourCorners = [
  createHandle([top, left]),
  createHandle([top, right]),
  createHandle([bottom, left]),
  createHandle([bottom, right]),
];
let fourSides = [
  createHandle([top]),
  createHandle([left]),
  createHandle([right]),
  createHandle([bottom]),
];
let twoSides = [
  createHandle([left]),
  createHandle([right]),
];

function createBox(shape, handles) {
  // 20 строк кода
}

let Rectangle = createBox(Shapes.Rectangle, fourCorners);
let Oval = createBox(Shapes.Oval, fourSides);
let Header = createBox(Shapes.Rectangle, twoSides);
let TextBox = createBox(Shapes.Rectangle, fourCorners);

То, что у меня получилось, было в два раза меньше того, что написал коллега. В моём варианте программы полностью отсутствовали повторяющиеся фрагменты! Чистейший код. Если нужно было изменить поведение системы, относящееся к конкретному направлению, или к конкретной фигуре, можно было сделать это в одном месте, а не переписывать несколько методов.

Была уже поздняя ночь (я увлёкся). Я влил результаты рефакторинга в ветку master и отправился спать, гордый тем, как я «причесал» неопрятный код коллеги.

Следующее утро


…прошло не так, как ожидалось.

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

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

Это — одна из ступеней развития программиста


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

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

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

Теперь я понимаю, что у моего «рефакторинга», и у того, как я к нему подошёл, было два серьёзных недостатка:

  • Во-первых, я не поговорил с тем, кто написал код. Я переписал код и залил его в репозиторий, не обсудив изменения с автором кода. Даже если мои изменения улучшали бы программу (я больше так не считаю), это — демонстрация совершенно неправильного подхода к подобным делам. В основе здоровой команды программистов лежит постоянное укрепление доверия. А если переписать код одного из членов команды и с ним не посоветоваться — это нанесёт удар по возможности эффективной совместной работы над проектом.
  • Во-вторых, за всё надо платить. Мой код пожертвовал возможностью менять требования в угоду сокращения объёма дублирования. Цена этой жертвы была слишком высока. Например, позже нам понадобилось обрабатывать множество особых условий и вариантов поведения для различных маркеров разных фигур. Мою абстракцию для поддержки подобных требований пришлось бы основательно усложнить. А вот в исходную «неаккуратную» версию кода подобные изменения вносились легче лёгкого.

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

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

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

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

Пусть идея чистого кода станет вашим ориентиром. А потом — отпустите эту идею.

Уважаемые читатели! Как вы относитесь к «чистому коду»?

RUVDS.com
RUVDS – хостинг VDS/VPS серверов

Похожие публикации

Комментарии 151

    +104
    Мне кажется или реальноая проблема статьи не в чистом/грязном коде, а в том что ктото пушит в мастер без mr/pr и кодревью?
      +16
      Нет, не кажется. В обсуждениях оригинальной статьи в твитере к это тоже был первый комментарий.
        +38
        Пуш с ревью — как секс в презервативе)) ощущения не те!
          +14
          Работа она ради денег, а секс ради денег — лучше в презервативе делать)) Я не в теме, но таковы мои предположения
            +10
            А вы докопались до истины))) Те кто прогают для удовольствия, те просто пушат в мастер, а кто для денег — для тех кодревью, аджайлы и прочая))
              +2

              Ну да. В пэт-проекте у меня только мастер.
              А на работе — тыща веток, только в одну из которых (по имени develop) можно пушить.

        +26
        Причем пушат похоже все: и автор оригинальной статьи, и его коллега (иначе чистоту кода можно было обсудить еще на ревью первоначальных изменений)
          +1
          А как не пушить в мастер, если менеджер говорит, через неделю сделать бету, хотя проекту только неделя и прошла?
          В былые времена я бы мог ожесточенно спорить с менеджером и говорить, что это тупо и не возможно. Сейчас я говорю «это будет не просто сделать, но мы сделаем все, что будет в наших силах». Вроде бы и не соврал и позицию обозначил.
            +1
            если вы один программист на проекте то можно и в мастер конечно, в таких условиях
            во всех остальных случаях деплой когда в мастере непонятно что и в каком статусе — это хождение по минному полю
              0
              Мы недавно перешли на Trunk Based Development и пока ничего не сломалось. Благодаря Feature Toggles деплой и релиз происходят отдельно один от другого.
              Есть ряд компаний, тот же Facebook, где весь код в одном репозитории и в одной ветке.
                +1

                А вы в мастер именно пушите или делаете пулл реквест?

          +1
          Похоже что так и есть. Ибо первая проблема — пуш в мастер без согласования с автором, вторая — модификация кода без code review. Этих проблем можно было бы избежать — обсуди автор заранее изменения в коде
            +2

            Чисто интересно "пуш в мастер без согласования с автором"
            это единстевенный подход и парадигма? Trunk-based никак?

              +17
              + пуш на прод в ночь. Ещё бы в пятницу или перед праздниками и было бы кккомбо.
              +7

              ИСЧХ судя по статистике голосования за статью почти 80% прочитавших согласны с приведенными "фактами".


              Я понимаю, что человек старался и переводил, но кмк текст "не стоит чернил которыми написан".

                +4
                Много людей ищут оправдание своему говнокоду и стараются его возвести в идеологию.
                  +1
                  Проблема в том, что есть ненулевое количество разработчиков, которые подвержены карго-культу. Т.е. вместо того, чтобы уловить причинно-следственные связи в статье, они уловят факт — не нужно плодить «лишние» абстракции, надо использовать Ctrl+C/Ctrl+v N раз.
                  P.S. Нужно признать, что понимать — когда абстракция лишняя, а когда крайне полезная нужная — очень сложно.
                    +1
                    Лепят не только по причине карго-культа.
                    Профессионально пишущие на ФП часто жалуются на то, что приходится разбирать код в который кто-то из предшественников что-то налепил просто чтобы потренироваться в ФП.
                      +2
                      Вообще, профессионально пишущим нередко приходится разбирать код, в который кто-то из предшественников что-то налепил просто чтобы потренироваться. Издержки профессии…
                        0

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

                  +1
                  Я не плюсовал статью, но скорее по причине бесполезности перевода статей Даниила. Он и сам их вполне успешно переводит.
                  Но не соглашаться со статьей тоже трудно.
                  Во-первых, я не поговорил с тем, кто написал код. Я переписал код и залил его в репозиторий, не обсудив изменения с автором кода.

                  Ну да, не прав. Странно, что у них не было код-ревью, но судя по тому что Redux-у уже пять лет, самой истории легко может быть и все десять. У меня на работе в это время VSS использовался вместо системы контроля версий. А ревью было добровольное и производилось путем подзывания другого разработчика за свой стол.
                  Во-вторых, за всё надо платить. Мой код пожертвовал возможностью менять требования в угоду сокращения объёма дублирования. Цена этой жертвы была слишком высока.

                  То же вроде верно.
                  Можно еще пошутить, подход Redux'а это прямо идеал кода «ДО».
                    +4
                    То же вроде верно.

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


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


                    20 минут на нетбуке в процессе просмотра фильма по зомбоящику. Каскадного изменения никакого нет, и даже те места, которые приходится менять, локализованы внутри моего небольшого неймспейса. Единственное ломающее изменение — это добавление параметра к BakeBread на втором этапе. Хотя и здесь можно было бы ничего не ломать, а просто использовать первую попавшуюся свободную печь. Это — банальная абстракция, которая позволяет извиваться змеей, когда надо с одной стороный вополнять новые требования, добавляя функционал, а с другой — обратная совместимость. К тому же вырабатывается некоторый интерфейс системы, который потом может быть использован при создании публичного API.
                      +3
                      Вы пишете одну функцию, которую используете из разных мест.


                      Вы как-то исходите из того, что юный Абрамов реально код укоротил и упростил.
                      Я вот рассказ понял наоборот, что он ввел в код новую абстракцию, причем выдуманную на ходу, не имеющую какого-то математического смысла. Для реального упрощения там можно было пытаться найти какой-то фитзический смысл этих операция, вроде предложенного TheShock'ом здесь.
                      Вместо этого у него вышла просто фабрика кусков функций и странных подобранных констант вроде fourCorners и twoSides, который не пойми как вычисляются.

                      Код в духе Хаскельного матчига по печкам/фигурам там был по-ходу до этого. А Абрамов начал его превращать в абстрактную фабрику шаблонов бобов-одиночек геометрических фигур. «Сначала берем рецепт сторон, потом углов, потом понимаем, что у заголовка сторон, не четыре, а две (sic!) и все пишем заново.» И из-за большого числа геометрических объектов стало формально короче. На деле нечитаемое говно. Человек-архиватор.

                      У меня есть еще вариант, что математический смысл в его действиях всё-таки был, но его никто не понял. Тогда опять же стоило сначала спросить у того, кто писал в начале.
                  +2

                  Интересно, какой процент программного кода проходит review перед публикацией? Я вот ни разу не работал в подобной компании и при взаимодействии с другими компаниями тоже ни разу не видел подобной практики (как и повсеместного использования юнит-тестирования, функционального тестирования и прочих "священных коров"). Это мне так повезло или в интернете некий карго-культ качества, который в реальности встретить надо ещё постараться?

                    +2

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

                      +3

                      Всеобщей статистики у меня нет, но code review в том или ином виде последние лет десять по моему на всех фирмах был. Под "в том или ином виде" я имею ввиду что иногда он был обязательным и без него было вообще не закоммитить, а иногда ты сам мог решать надо кому-то дать код ещё раз глянуть или нет.


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

                        0

                        Работал в одной Большой компании, которой лет сорок, у которой есть довольно много клинетов, и которая славится тем, что не падает. В общем, в ней внезапно решили, что теперь код-ревью становятся обязательными. И был у меня среди коллег один товарищ с ну очень интересным ЧСВ. В общем, он создавал бранч, пушил туда, создавал PR, назначал себя ревьювером, сам писал LGTM, сам мержил, сам хлопал себя по плечу. И так несколько лет. Может, до сих пор так делает, не знаю.


                        Впрочем, это не лучшее его качество было.

                          +2
                          А я, наоборот, не работала на проектах, где бы ни было code review в том или другом виде. А сейчас при всем желании в битбакете не могу смержить pull request с продовым бранчем без одобрения кого-то из коллег.
                            +2

                            У нас в компании запрещено мержить бранч в мастер без ревью.

                              +4
                              некий карго-культ качества

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

                              после этого начнёт появятся понимание что качество — оно нужно и строится оно на очень многих параметрах. Это как в каске по стройке ходить, если взять статистику падения кирпичей на головку — каска с вашей точки зрения это каргокульт
                                +2
                                Это вам повезло. У нас невозможно смерджить pull request без отметки ревьювера. И знаю множество компаний с таким же подходом.
                                • НЛО прилетело и опубликовало эту надпись здесь
                                    +2
                                    Просто переход на обязательные ревью я стал встречать лет пять назад. Нормальные (когда на ревью 1000 строк ревьюер убивает день, проверяет решение целиком, а не тупо работает линтером, проводит минимальное тестирование) я и сейчас не везде вижу. Ну и нормальные ревью, а не «10 lines of code = 10 issues, 500 lines of code = looks fine» это не дёшево.
                                      +1

                                      За пять лет был только один такой проект — который полностью писали с нуля вдвоём с коллегой. Когда команда больше — куда ж без ревью.

                                    +6
                                    Не так страшен грязный код, как страшен стабилизированный годами грязный код, который начал работать.
                                      +24
                                      Во-вторых, за всё надо платить. Мой код пожертвовал возможностью менять требования в угоду сокращения объёма дублирования. Цена этой жертвы была слишком высока. Например, позже нам понадобилось обрабатывать множество особых условий и вариантов поведения для различных маркеров разных фигур. Мою абстракцию для поддержки подобных требований пришлось бы основательно усложнить. А вот в исходную «неаккуратную» версию кода подобные изменения вносились легче лёгкого.

                                      То есть проблема в том, что была написана некорректная абстракция и залита в обход код овнера, но попрощались вы с чистым кодом? Оооооооок

                                        +11
                                        попрощались с идеей существования универсальных и безусловных догматов и вступили в реальный мир вариативного поведения.
                                          +10
                                          Я скорее согласен с вами, чем наоборот, но…
                                          Любая удачная для СЕЙЧАС абстракция может превратиться в НЕКОРРЕКТНУЮ через пару изменений. К этому нужно быть готовым. Когда вы раскладываете ПО на абстракции, вы тем самым создаете архитектуру своей программы (нужно делать вот так, и вот так...). Но любая архитектура вносит свои ограничения. Был свидетелем внедрения 1С. Вроде архитектура построения на регистрах и документах замечательна, и многие задачи очень легко на неё раскладываются. А потом всплыли задачи, которые на эту архитектуру ложатся плохо. И пришлось ломать эту 1С пытаясь сделать из Феррари танк Т-34. Конец истории печален.
                                            +1

                                            У нас такое было с Odoo. В итоге после того, как смотришь назад, приходит понимание, что лучше было с самого начала пилить с нуля :)

                                              0
                                              Пилить с нуля без Odoo?
                                                0

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

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

                                                Я бы так не сказал. На мой взгляд абстракция это скорее редукция на минимально необходимый в каком-то конкретном контексте набор параметров/аттрибутов.

                                                  0
                                                  Тогда уж
                                                  абстракция это скорее редукция на минимально необходимый в каком-то конкретном контексте набор понятий\инструментов

                                                  И этот набор должен покрывать потребности контекста. Т.е. и быть универсальным внутри вариабельности этого контекста.
                                                    0

                                                    Но при этом у вас в любом случае идёт редукция и ваш "набор" всё равно будет менее универсальным чем исходный.

                                                      0
                                                      Да не вопрос. Я скорее уточняю, недели спорю.
                                                    0
                                                    тогда уместнее аналогия: набор ключей и разводной ключ.
                                                    разводной, конечно, книверсален.
                                                    но нормальным ключем удобней
                                                      0

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

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

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

                                                            0

                                                            Присохшие и ржавые — скорее сорвёшь, чем открутишь.

                                                              0
                                                              вот и я о чем говорю
                                                      +1
                                                      Эх, если бы просто проигрывает. Бывает и так, что эта самая абстракция вообще не позволяет решить задачу, или придает решению такую алгоритмическую сложность, которая вместо 20 секунд требует 20000 лет на выполнение.
                                                  +4
                                                  Моё мнение: код как раз не был чистым.
                                                  Если вы сокращаете код, убирая «дубликаты» но нарушаете single responsibility, то к очищению кода это отношения не имеет.
                                                  Чистка кода — это приведение его к его естественному, минимальному и красивому виду. Но мой взгляд, код коллеги как раз был ближе к своему естественному виду.
                                                    +6
                                                    мне всегда нравятся такие субъективные прилагательные как естественный, минимальный и красивый. Если начинаешь спрашивать что это значит в данном конкретном случае, большинство людей отвечают по разному
                                                      +1
                                                      А что вы хотите услышать в ответ на такой вопрос? Жесткой логической выкладки вы не получите, так как нельзя «проверять алгеброй гармонию».
                                                      Да, человек умеет мыслить (и извлекать из этого пользу) нечеткими понятиями. То, что они не передаваемый от одного человека к другому — это другое дело.
                                                        0

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

                                                        +1

                                                        Что мешало сделать одну функцию, которая вызывалась из других, а когда логика поменялась, вместо натягивания совы на глобус в этот момент сделать новую функцию?


                                                        А автор после своего "прощай, чистый код" видимо откроет для себя прелести ущербно-ориентированного программирования.

                                                          0
                                                          Именно так, и правильно. Но ведь автор сделал не совсем так.
                                                        +31
                                                        Я, как человек, который минимум дважды решал задачу, которая описана в топике совершенно не понимаю, откуда может взяться различие между движением (к примеру) левого и правого маркера. И откуда может взяться 10 строк однотипных вычислений для каждого маркера Rectangle. Звучит как лютый ужас.

                                                        Я предполагаю, что изначальная проблема совершенно в ином — крайне низком техническом уровне команды, которой приходится извращаться с довольно простыми вещами, чтобы оно хоть как-то заработало. Оттуда и такое грубое нарушение DRY
                                                          0
                                                          >откуда может взяться различие между движением (к примеру) левого и правого маркера.
                                                          Если мы конечно имеем в виду одно и тоже (что без картинки не всегда просто), то легко — для любой модификации фигуры, отличной от параллельного перемещения. Например вращение, или сдвиг. Двигаться маркеры как раз могут по разным траекториям. Другое дело, что формулы скорее всего должны быть одинаковые (и включать координаты маркера, например, и центра вращения).
                                                            +3

                                                            Ну это же все запросто обобщается простейшей векторной математикой.

                                                              +2
                                                              А я что-то другое написал? Траектории маркеров — разные, формулы — скорее всего общие.
                                                            +5
                                                            А потом они создали отдельную сущность для перевернутого на 90 градусов Rectangle.
                                                              0
                                                              Я бы (если бы позволила задача) наверное просто поместил бы фигуру в «user interface rectangle». И отдельно менял положение фигуры внутри этакого «интерфейс-контейнера» \ отдельно положение контейнера во фрейме — тогда перетаскивание маркеров вправо\влево вверх\вниз делалось бы вообще одной строкой.
                                                              Но после этого дисклеймера могу выступить адвокатом дьявола:

                                                              1. Насколько я помню (но не понимаю, только видел разъяснения) модели при замене i на -i не полностью эквивалентны. Может быть у ребят именно настолько чувствительная к математической обработке модель.

                                                              2. 10 строчек кода при перетаскивании левого (правого \ верхнего \ нижнего...) края могут появиться, если это действие определено (условно) как scale + сохранение геометрического центра — ну то есть тогда надо сначала раздвинуть размеры фигуры по оси, найти новое положение центра, переместить новое положение центра, пара комментариев, пара пустых строк… 10 наверное наберётся
                                                              // не спрашивайте меня — не знаю ей богу откуда берётся одновременно 10 строк кода для перемещения левого края и отдельная функция для каждого действия.
                                                              +7

                                                              Статья весьма органично сочетается с другим креативом автора — Вещи, которых я не знаю в 2018

                                                                +1

                                                                А это тот самый Дэн Абрамов, который у многих кумир?

                                                                  0

                                                                  Ага, тот самый

                                                                +6
                                                                Автор очень вольно обращается с выражением «чистый код», по сути подменяя понятия. А мораль истории на самом деле здесь:
                                                                позже нам понадобилось обрабатывать множество особых условий и вариантов поведения для различных маркеров разных фигур. Мою абстракцию для поддержки подобных требований пришлось бы основательно усложнить.
                                                                Стремиться надо к простоте (с учётом дальнейшего развития проекта), а не к абстракции.
                                                                  +3
                                                                  Абстракции(правильно построенные) зачастую упрощают как понимание, так и изменение кода. Куда проще понять вызов одного и того же метода с разными параметрами, чем в 4 похожих местах искать чем они отличаются. Абстракции проще тестировать (нужно объяснять почему?). После вызова метода который реализует абстракцию вы можете скорректировать его результат в определённых случаях, либо делать не один, а несколько методов для боле точечного управления, планирования мест добавления кода в будущих изменениях и соблюдения единственной ответственности.
                                                                    0
                                                                    Именно. Абстракции должны использоваться как способ упрощения, но не должны становиться самоцелью.
                                                                  +8
                                                                  Всю статью можно свести к «Перед тем как изменить странный код своего коллеги, спросите у него, почему он написал так. Возможно, здесь есть смысл».
                                                                    +10

                                                                    От человека, "подарившего" миру redux.

                                                                      +7
                                                                      А, Дэн Абрамов. Это многое объясняет. Во многих его проектах видно чрезмерное усложнение подходов. Припоминаю библиотеку для реализации drag-and-drop, которая требует огромной обвязки, но при этом не позволяет делать базовых вещей, вроде сохранения размеров строк в таблице при перетаскивании. А уж стек технологий под Redux это просто песня. Совершенно не уверен, что код примера в итоге упростился и улучшился. Почему не делегировать всю работу с рамкой отдельному компоненту, я не очень понял. Повторение одного и того же в 10 строках по несколько раз ничем не может быть оправдано, на мой взгляд, так что изначальный код — просто плохо написанный код, по-любому требующий рефакторинга.
                                                                      +8

                                                                      Там в оригинале Let clean code guide you. Then let it go. Откройте свой проект. Посмотрите на код. Нравятся ли вам решения которые вы принимали с год назад? Показали бы вы его своему будущему работодателю? А потом Let it go.
                                                                      А вообще, очень странная статья которая больше тянет на кликбейт.

                                                                        –1

                                                                        del

                                                                          +6
                                                                          Стремиться нужно не к «чистому» коду, а к понятному. Чтобы пришел любой Вася после Пети смог оперативно что-то исправить/доработать.
                                                                            +4
                                                                            А ещё к поддерживаемому. Чтобы каждое изменение и багфикс не требовало вносить одинаковые правки в 8 одинаковых участков кода в разных местах
                                                                              0
                                                                              Я очень хочу увидеть такой код. Можно ваш вариант в студию. Но при этом код должен позволять менять один элемент не затрагивая другие.
                                                                                0

                                                                                Ну давайте сформулируем так: стремиться к такому коду, который при изменениях и багфиксах требует правки в минимальном количестве мест :)

                                                                                  +1
                                                                                  Я понимаю, о чем вы говорите. Сам часто стремлюсь к такому адаптивному решению. Когда поменять 3 параметра, и весь код сам перестроился под эти изменения.
                                                                                  Но у такого подхода есть слабые места.

                                                                                  Предположим, что у нас есть некие Круг, Квадрат, Треугольник. И мы их рисуем.
                                                                                  Сначала рисуем Треугольник, как базовый элемент.
                                                                                  Замечаем, что Квадрат от Треугольника отличается всего 2-мя строчками. Поэтому для рисования Квадрата сначала вызываем рисование Треугольника, а затем пишем те самые 2 строчки наверх. С Кругом та же история, его мы рисуем через Квадрат.
                                                                                  Вроде все красиво, изменение (добавляем цвет например) внесенные в Треугольник, скажутся и на двух других. Минимальность изменений есть.
                                                                                  Но вот теперь нам нужно внести изменения в рисование Квадрата, но так чтобы это не затронуло Круг. И начинаются проблемы.
                                                                                  Когда у вас 3-4 связанных таким образом элемента — это ничего. А вот когда их уже 10, и при том дерево иерархии не простое, придется каждый раз проверять, что изменения в одном месте корректно перекрываются собственным кодом других элементов. Это ещё тот геморрой. По сравнению с которым, 8 раз копипастом ставить один и тот же код — это сущие пустяки.
                                                                                    +1

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


                                                                                    И в данном случае наверное надо было бы не "рисовать квадрат через треугольник", а выделить "геометрическую фигуру" и уже через неё идти к треугольнику, квадрату и кругу.


                                                                                    Но естественно это всё здорово рассуждать в теории и/или постфактум :)

                                                                                      +4
                                                                                      Замечаем, что Квадрат от Треугольника отличается всего 2-мя строчками. Поэтому для рисования Квадрата сначала вызываем рисование Треугольника, а затем пишем те самые 2 строчки наверх. С Кругом та же история, его мы рисуем через Квадрат.
                                                                                      Вы довольно грустно представляете себе реюз кода. Не удивительно, что имеете проблемы с абстракциями.

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

                                                                                      И хотя пример абстрактный — подход такой реально работает в сложных системах с плавающими правилами — играх.

                                                                                      Больше двух копипаст кода — причина для срочного рефакторинга (исключение — стабильные бранчи).
                                                                                    +1
                                                                                    Можно ваш вариант в студию
                                                                                    Вариант чего, простите? В топике написано 8 названий методов, а внутри — комментарии про 10 строчек кода.

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

                                                                                    Но при этом код должен позволять менять один элемент не затрагивая другие.

                                                                                    1. Зачем в ресайзере картинки одна из четырех точек должна иметь другое поведение, чем остальные? Чтобы запутать пользователя?
                                                                                    2. Если будет полигон из 100 точек — дублируем код 100 раз? Вдруг одна из этих точек должна будет получить иное поведение?

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

                                                                                    8 раз дублировал код очевидный идиот. И видимо его нанял точно такой же идиот, который заворачивает на код-ревью правильные направление ещё и, судя по топику, без объяснения.

                                                                                    Я только один раз видел процесс описанный в топике. Сразу же после этого я написал заявление на увольнение.
                                                                                      +2
                                                                                      1. Зачем в ресайзере картинки одна из четырех точек должна иметь другое поведение, чем остальные? Чтобы запутать пользователя?

                                                                                      Вариантов куча: так требует заказчик, так просит пользователь, или из МинФина пришло постановление, что нужно оформлять именно так…
                                                                                      И четко выверенная абстракция трещит по швам.

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

                                                                                      Пока функционал фигур ещё сырой, и может быть дополнен\изменен, выгоднее держать код по отдельности. Давайте признаемся, что экономия в 10 секунд на 7 копипастов (а вы знаете куда вставлять код) вас особо не спасет. Это раз.
                                                                                      Два, каким бы вы гениальным программером не были, заранее предусмотреть все случаи и особенности (чтобы раз написал и все) вы не сможете. Код часто переписывается с нуля, когда в нем набирается критическая масса несоответствий выбранных абстракций и реальности.

                                                                                      Вариант чего, простите?

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

                                                                                      В топике написана очевидная глупость.

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

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


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

                                                                                        В теория я с вами согласен. В случае с данным конкретным примером нет. Потому что в данном кокретном случае можно как минимум с вероятностью стремящейся к 100% предположить что правильнее будет первый вариант.


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

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

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

                                                                                          Про конкретно обсуждаемую проблему(конкретный случай в статье) никто и не говорит. Говорят о схожих ситуациях.
                                                                                          У меня например были все 3 причины. И если от первых двух ещё можно отмазаться, то с постановлением правительства не поделаешь ничего.
                                                                                          В теория я с вами согласен. В случае с данным конкретным примером нет.
                                                                                          А у меня наоборот. Я был в теории явным сторонником вашей точки зрения, но практика поставила меня на место.
                                                                                          В случае с данным конкретным примером нет.

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

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

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

                                                                                            0
                                                                                            У меня например были все 3 причины. И если от первых двух ещё можно отмазаться, то с постановлением правительства не поделаешь ничего.

                                                                                            "Все 3 причины" в вопросах того как конкретно должны выглядеть классы в вашем коде?


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

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

                                                                                              +3
                                                                                              «Все 3 причины» в вопросах того как конкретно должны выглядеть классы в вашем коде?

                                                                                              Нет. Я как представитель новых подходов старался найти общее, предусмотреть различные варианты, уменьшить код. Результатом чего были абстракции, полностью покрывающие потребности. Из них удобно собиралось то, что мне надо. Но потом приходило новое требование, которое рушила мою стройную архитектуру. Соответственно, мне приходилось усложнять подходы. И иногда это усложнялось настолько, что перекрывало все возможные выгоды.
                                                                                              А рядом был коллега из старой школы. Он понятия не имел ни про SOLID, ни про другие течения. Он писал код в лоб, и имел не редко 10 файлов, в которых было до 50% общего кода. И пока я рефактил свой код (а там серьезно подумать надо), он быстро вносил изменения в свой ничем не обремененный код без архитектуры. В итоге он после работы гулял, а я вечерами сидел за кодом.
                                                                                                +1

                                                                                                Ну так это уже всё-таки немного о другом. И я никогда и не спорил что код можно и "переоптимизировать".


                                                                                                П.С. И когда у кого-то "свой код", то это одна ситуация, а когда у нас "общий код" для кучи народа то совсем другая. Особенно если эта куча народа ещё и работает в куче команд да ещё и территориально удалены друг от друга.

                                                                                                  +3

                                                                                                  У меня тоже был коллега, который не имел понятия про SOLID, имел для одной задачи с десяток функций с именами вида compute, compute2, compute_old, compute_tmp, compute2_aux и так далее, с примерно похожими именами параметров и локальных переменных, с нулевым форматированием, через которые продраться невозможно.


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

                                                                                            +3
                                                                                            Вариантов куча: так требует заказчик, так просит пользователь, или из МинФина пришло постановление, что нужно оформлять именно так…
                                                                                            Окей. Этому есть причина. Мы её обсуждаем, анализируем, рефакторим. Я работаю в геймдеве, тут каждый спринт может поменяться всё, что угодно

                                                                                            или держать код для фигур по отдельности (его коллега)
                                                                                            Нет, не держать код для каждой фигуры. А код для каждой точки каждой фигуры. Его коллега — или новичок, или идиот. Начальник — или саботажник, или идиот.

                                                                                            Давайте признаемся, что экономия в 10 секунд на 7 копипастов (а вы знаете куда вставлять код) вас особо не спасет
                                                                                            А когда предел? 15 копипастов? 1000 копипастов? Для меня предел — между 2 и 3. Два копипаста — сомнительно, но позволительно. 3 копипаста — совершенно недопустимо.

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

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

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

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

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

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

                                                                                            Держать в декомпозированном состоянии всё приложение на случай если одна из сотен тысяч шестерёнок может потребовать замены — до губительности глупый подход, ведь ради удобства замены одного из 100000 мест мы жертвуем удоством работы с остальными 99999 местами.
                                                                                              0
                                                                                              Я вижу 2 варианта, или объединять все возможные фигуры одной абстракцией (как сделал автор), или держать код для фигур по отдельности (его коллега).

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

                                                                                                0
                                                                                                Ваш вариант — это не третий, это смесь первого со вторым. Те фигуры, которые имеют общий код будут страдать одними недостатками, а те которые описаны уникальными другими.
                                                                                                Каждый раз, когда вы делаете изменения, вам придется восстанавливать в памяти уникальная ли эта фигура или нет, обращаться к диаграмме этих фигур (если вы догадались ей сделать). И эта одно из причин почему я бы хотел делать или все абстракцией, или все уникальным.
                                                                                                Но дело не столько в этом.
                                                                                                Абстракция пускает корни дальше по коду. Фигуры объедены некой абстракцией Х. И в качестве аргумента дальше вы и передаете эту самую Х. И теперь, Бац, и какая-то фигура перестала вмещаться в абстракцию, и для неё нужен отдельный код. Этакая заплатка, покрывающая уникальность этой фигуры. Отличие фигуры небольшое, меняем абстракцию. Затем изменения нужно внести в другую фигуру, но уже её легче действительно писать уникальной. Получается, что введенная кривость (ради первого изменения) абстракции нас не спасла, и она останется в нашем коде. В итоге код больше походит на набор костылей. На рубашку, которую заплатками и перешейками пытаются превратить в итальянский костюм.
                                                                                                А вот если признаться сразу себе, что никаких абстракций здесь не будет, то результирующий код будет опрятным, хотя и избыточным.

                                                                                                ПС: Хотя я именно так и делаю. Делаю общую абстракцию, и дополняю её специальным классом-заплаткой, который должен запускаться когда код нельзя вместить в базовые ограничения и правила. Если у фигуры этот класс =null запускается обычный обработчик, а если нет, то вместо обычного обработчика запускается класс заплатка.
                                                                                                  0
                                                                                                  Каждый раз, когда вы делаете изменения, вам придется восстанавливать в памяти уникальная ли эта фигура или нет, обращаться к диаграмме этих фигур (если вы догадались ей сделать).

                                                                                                  А что мне помешает просто посмотреть в код?


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

                                                                                                  Не вижу каким образом конструкция


                                                                                                  switch (type) {
                                                                                                    case 'figure1': ...;
                                                                                                    case 'figure2': ...;
                                                                                                    case 'figure3': ...;
                                                                                                    case 'figure4': ...;
                                                                                                    case 'figure5': ...;
                                                                                                    case 'figure6': ...;
                                                                                                    case 'figure7': ...;
                                                                                                    case 'figure8': ...;
                                                                                                    case 'figure9': ...;
                                                                                                  }

                                                                                                  может оказаться более опрятной, нежели


                                                                                                  switch (type) {
                                                                                                    case 'abstractFigure1': ...;
                                                                                                    case 'abstractFigure2': ...;
                                                                                                    case 'figure3': ...;
                                                                                                  }
                                                                                                    +1
                                                                                                    С конца.
                                                                                                    switch в эпоху ООП — это антипатерн. Если вам нужно будет внести новую фигуру в код, представте по скольким операторам switch нужно пройтись…

                                                                                                    А что мне помешает просто посмотреть в код?
                                                                                                    Простая ситуация, коллега спрашивает меня, «сможет ли код обработать такую ситуацию» или «чтобы будет, если внести такое-то изменение».
                                                                                                    Вариант А. У меня все построено по единым правилам, и тогда я в уме могу за 3 секунды прокрутить, и дать коллеге ответ. А потом продолжить свои дела не потеряв мысль.
                                                                                                    Вариант Б. Мне приходится вспоминать что на это может повлиять, лезть в код (а это может быть другой проект, не тот, который открыт у меня). В итоге коллега ответ получает. Но я полностью сбит с мысли на своей работой, и мне приходится вспоминать, что я хотел сделать.

                                                                                                    Главная проблема программирования для человека — это невозможность удержать в голове большую структуру одновременно, как единое целое. Знаменитое число 7+\-2 объектов памяти. Пока сущность невелика, я могу удержать ей в памяти я генерирую код практически мгновенно. Когда же код разрастается и удержать его невозможно, КПД резко падает. Так вот, единые правила позволяют мне удерживать в уме в разу большие структуры, чем те, у которых есть исключения.
                                                                                                    Абстракция — это как раз и есть попытка оградить человеческий ум от ненужной для данного вопроса информацией. Но это работает пока у этой абстракции нет исключений.
                                                                                                      +1
                                                                                                      switch в эпоху ООП — это антипатерн. Если вам нужно будет внести новую фигуру в код, представте по скольким операторам switch нужно пройтись…

                                                                                                      Замените switch на любой костыль, который вы будете использовать в варианте 2, суть от этого не поменяется.

                                                                                                        +7
                                                                                                        switch в эпоху ООП — это антипатерн.

                                                                                                        Ну да, обязательно нужно написать визитор с пачкой фабрик, ведь без этого статическое перечисление объектов никак не обработать.


                                                                                                        Вот это — карго-культ, а не то что там выше пишут.

                                                                                                          +1
                                                                                                          Простого наследования вполне достаточно.
                                                                                                          Попробую ответить по другому на вопрос «какой из вариантов лучше?»
                                                                                                          — Мне одинаково не нравятся оба варианта.
                                                                                                            +3

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

                                                                                                            0

                                                                                                            Если оператор case (switch) по типам объекта — можно написать 1 виртуальный метод и втыкнуть его вызов в код вместо case. И написать реализацию этого метода для требуемых классов.

                                                                                                              +2

                                                                                                              Чтобы что? Я же подчеркнул, что речь про статические иерархии.


                                                                                                              Посмотрите как сделано представление AST в любом языке. Там визиторы с миллионом методов VisitConstant/VisitExpression/… При том что это всего лишь эмуляция АДТ в языках без соответствующих средств.


                                                                                                              Вопрос — зачем эти сложности если есть возможность обойтись простым средством? Для каждого инструмента своя задача. Если я захочу получить тип монстра, и если это человек то удвоить его урон, если демон — то отправить ракету на марс, а в остальных случаях ничего не делать — какой виртуальный метод поможет мне этого добиться?
                                                                                                              virtual int GetDoubleDamageOrLaunchRocketOrDoNothing()?

                                                                                                                0
                                                                                                                Если нужно «применить» урон к объекту некоего класса, вызываем его виртуальный метод ApplyDamage(Value: Integer);
                                                                                                                И нам в вызывающем методе всё равно кто это. В реализации этого метода в каком-то объекте будет одна строчка Dec(LifeLevel, Damage*2); а для другого объекта метод не реализован, будет вызвана пустая заглушка из класса в корне иерархии.
                                                                                                                И поскольку урон может быть нанесён объекту разными методами в разных местах, везде где требуется будет вызываться этот метод ApplyDamage, а не копипаститься код switch по типам объектов.
                                                                                                                Но я больше по бизнес-приложениям, чем по геймдеву. Например, термин «статическая иерархия» мне непонятен, что вы имели ввиду.
                                                                                                                  +1
                                                                                                                  Если нужно «применить» урон к объекту некоего класса, вызываем его виртуальный метод ApplyDamage(Value: Integer);

                                                                                                                  Так что насчет запуска ракеты? Она не запустилась. Или метод ApplyDamage должен её запускать?


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

                                                                                                                  Ну я же привел пару примеров, например иерархия Expression. Вместо виртуальных методов там вполне себе обычные VisitXXX на каждого наследника, которые являются средством эмуляции АДТ в ООП языках.

                                                                                                                    0
                                                                                                                    В методе ApplyDamage для всех объектов будет описана реакция этого объекта на урон. Если реакция заключается в запуске ракеты — да, объект демон запустит ракету.

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

                                                                                                                      То есть метод будет иногда возвращать мусорный int, и запускать ракету, иногда возвращать число (я напомню, что по постановке задач нам нужно получить число, а не нанести какой-либо урон), а остальные реализации будут видимо магическое "-1" возвращать потому что по задаче им не надо делать ничего.


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

                                                                                                                      Есть.


                                                                                                                      • Если у вас иерархия динамическая, то есть она расширяется, но при этом набор функциональности у них совпадает — делается обычное оопшное наследование, а "заменой условий полиморфизмом" и прочими заметками из чистого кода
                                                                                                                      • Если у вас статическая иерархия, то АДТ это наш выход. Всегда можно поматчиться по значениям и что-то полезное сделать.
                                                                                                                      • Если динамическая и иерархия, и функциональность — ну, тут уже ничего не попишешь, таглес файнал энкодинг, лучше ничего еще не придумали.
                                                                                                                        0
                                                                                                                        Чувствую, мы недопоняли друг друга в постановке задачи. Вы писали:
                                                                                                                        Если я захочу получить тип монстра, и если это человек то удвоить его урон, если демон — то отправить ракету на марс, а в остальных случаях ничего не делать

                                                                                                                        У вас эти действия выполняются прямо в коде switch. Я предложил передать их выполнение в виртуальный метод соответствующего объекта. Тогда возвращать из ApplyDamage ничего не обязательно.
                                                                                                                          –1
                                                                                                                          Я предложил передать их выполнение в виртуальный метод соответствующего объекта.

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


                                                                                                                          Вы можете придумать сигнатуру виртуальной функции которая бы всё это делала?

                                                                                                                            +1

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


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

                                                                                                                              +1

                                                                                                                              Причем тут сманипулировать? Покажите, почему майкрософт вместо того чтобы написать классный виртуальный метод который бы всё сделал написали миллион 100500 AccpetThis и AcceptThat в базовом классе?

                                                                                                                                –3
                                                                                                                                Я же объяснил при чем.

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

                                                                                                                                Там тоже люди работают.
                                                                                                                                  +1
                                                                                                                                  Не знаю, может программисту МС было лень, или они решили соптимизировать, или вообще решили, что это малоприоритетная задача и отдали джуну.

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

                                                                                                                +1

                                                                                                                А теперь у вас добавляется взаимодействие двух объектов (скажем, одна фигура должна уметь прилипать к другой). Куда и какой виртуальный метод вы добавляете?

                                                                                                                  0

                                                                                                                  Я бы подумал о том, как мне удобно это формулировать.


                                                                                                                  Вот, например, если бы я писал текстовый документ, я бы поместил это в раздел "Треугольники", "Квадраты" или "Прилипание фигур" чтобы было компактно и понятно?


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


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


                                                                                                                  Если язык такого не поддерживает, приходится воплощать десятое правило Гринспена.

                                                                                                                0

                                                                                                                Да ну право, какие фабрики и заводы, это перебор. Вы удивитесь если узнаете что можно жить только if-ами без else-ов если правильно структурировать код.

                                                                                                  +2
                                                                                                  К сожалению, выделение абстракций, ориентирующееся только на существующий код, периодически приводит к тому, что для модификации приходится всю абстракцию переделывать заново. Ну или втыкать модификацию сбоку с каким-нибудь костылём.

                                                                                                  Так что абстракция помогает писать поддерживаемый код только в том случае, если у вас уже есть некоторое представление о том, что и как в нём нужно будет модифицировать.
                                                                                                    0
                                                                                                    Приблизительно то же говорят ExtremeCode на ютубе в роликах про наследование: ограничивать применение тем кодом, который не предполагается в будущем существенно модифицировать, типа фреймворков.
                                                                                                      +2
                                                                                                      Верно, сложность с самоповторами обычно возникает, когда ТЗ не прописано чётко изначально. В любом случае, я лично в процессе написания готов терпеть 2-3 самоповтора почти одинаковых участков кода. Потом у меня сдают нервы, и я оформляю этот блок хотя бы в подпроцедуру, с нужными параметрами. Благо Pascal/Delphi позволяет подпроцедуры внутри метода, не засоряя интерфейс класса.
                                                                                                        0
                                                                                                        когда ТЗ не прописано чётко изначально
                                                                                                        Подозреваю, оно так в 80% случаев. Хотя, да, ценность изначального чёткого ТЗ как инструмента разделения участков кода по уровням будущей изменчивости (и, соответственно, оптимизируемости) вы сейчас в моих глазах существенно повысили…
                                                                                                      0
                                                                                                      А ещё к поддерживаемому. Чтобы каждое изменение и багфикс не требовало вносить одинаковые правки в 8 одинаковых участков кода в разных местах

                                                                                                      В первом и втором предложении, на самом деле, указаны разные свойства. Более того — эти свойства на практике легко могут быть взаимоисключающими :)
                                                                                                      Т.е., либо простая поддержка — либо отсутствие дублирования. Но не вместе.


                                                                                                      В общем-то, по сути, об этом и статья — не всегда снижение дублирования ведет к упрощению поддержки.

                                                                                                        0
                                                                                                        Поддерживаю.
                                                                                                          +1
                                                                                                          Т.е., либо простая поддержка — либо отсутствие дублирования.
                                                                                                          Глупость какая-то.
                                                                                                          У вас есть восемь почти одинаковых точек и восемь кусков кода. Но 2 из них имеет дополнительное изменение в коде А, ещё одна дополнительное изменение в коде Б.

                                                                                                          Вам говорят добавить ещё три точки. Вы открываете и… копипастите код ещё 3 раза? Но какой код для копипаста выбрать? Если изменения не декларативные, а просто какой-то рандомный код
                                                                                                      +4
                                                                                                      Инструмент морф для растровых изображений — сдвигает пиксели изображения с силой обратно пропорциональной удалению центра кисти.
                                                                                                      Есть редакторы, где движения этого инструмента происходят в реальном времени. Потому как сам алгоритм достаточно простой.
                                                                                                      А вот в дорогих именных редакторах — слайдшоу. И теперь я знаю причину.
                                                                                                        –2
                                                                                                        Есть подозрение, что в обоих случаях размер исполняемого файла будет одинаковый, т.е. оптимизации на уровне, собственно, кода (машинного), — ноль.
                                                                                                        Просто исходник красивее с точки зрения педанта.
                                                                                                          0
                                                                                                          Там же вроде Javascript? Ну, может, особо умный JIT и сможет свести до одинакового кода, но есть сомнения.

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

                                                                                                            К сожалению, сужу по С/++. Там бывает тратишь-тратишь время на красоты и лаконичность, а объем кода с -o3 точно такой же. JS сейчас тоже очень хорошо оптимизируется.
                                                                                                          –1

                                                                                                          Согласен, все мы через это проходили. Обычный этап, когда хочется быть святее папы римского.
                                                                                                          Свой этап мне трудно комментировать — для меня-то казалось все правильно. Тоже удивлялся — чего эти старики копипастят код. Заметил уже позже у более молодых. Пришли из института ребята, "адепты чистого кода", как мы их позже прозвали. Вот у них это было: три строчки повторяются — в отдельную функу. Пофиг, что это была работа с разным железом, и на следующем новом девайсе эти строчки сильно поменялись. Итогом стал монстр с кучей ifdef'ов.

                                                                                                            –1
                                                                                                            Вот как раз сейчас начал увлекатсья принципами DRY и KISS.
                                                                                                              +3
                                                                                                              Особенно интересное начинается, когда они начинают противоречить друг другу.
                                                                                                              +1
                                                                                                              Ну, всё закономерно: рост упакованности кода снижает его перспективную гибкость и вариативность. Надо выбирать. Ну и ещё: уметь чистить код ваджо, тренировать это умение нужно, но умение выбрать и организовать адекваный момент для чистки кода — это умение более высокого порядка.
                                                                                                                +1

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

                                                                                                                  +3
                                                                                                                  У меня вот обратный процесс, который меня даже беспокоит — все меньше хочется красивого кода и даже правильных алгоритмов. Что-то более правильное например могу реализовать по соседству (например адаптер с JSON к новому скриптовому языку вместо самописного протокола упаковки данныз), но тратить немного времени времени на переписывание кода старого адаптера пока он удовлетворяет требованиям — жалко времени.
                                                                                                                  Даже изучать новые методики программирования /проектирования и новые технологии не хочется пока имеющихся хватает. Интересуют только конечные возможности софта. Возможно из меня так себе программист, а может дело в том, что я фрилансер и иногда сам себе заказчик…
                                                                                                                  P.S. выговорился и легче стало ;)
                                                                                                                    +1

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

                                                                                                                      0
                                                                                                                      В том то и дело, что эта конструкция в глубине пет-проекта и постоянно используется, однако она именно в глубине — для обеспечения межпроцессного взаимодействия. Для JSON конвертер переписать просто — неудобно его отлаживать.
                                                                                                                      Ваш ответ не показался в трекере, странно
                                                                                                                        0
                                                                                                                        del
                                                                                                                      +2
                                                                                                                      Скоро программирование перерастет в философию и архитекторы высшего порядка будут днями на пролет обсуждать и осмысливать как правильно сделать рисования кругов и квадратов.
                                                                                                                        +3

                                                                                                                        В прошлом году в статье было 100+ комментариев под статьей на тему того должны ли быть методы "hero.attack(monster), monster.attackedBy(hero) или GameMaster.Attack(hero, monster)". Такие дела.

                                                                                                                          –1
                                                                                                                          т.е. вместо того чтобы просто взять и сделать хоть как нибудь, будут мусолить эту тему.
                                                                                                                            0
                                                                                                                            Вот чем хорош CLOS с multiple dispatching'ом…
                                                                                                                              0

                                                                                                                              И любой ML-style ФП-язык с паттерн-матчингом.

                                                                                                                            0
                                                                                                                            Это и так происходит постоянно. И из этих облачных высей регулярно сваливаются вкусняшки.
                                                                                                                            Главное — понимать, сколько ресурсов отводить на астронавтику, а сколько на «пиши код б… ь».
                                                                                                                              –1
                                                                                                                              Вместо того, чтобы признать, что стандартное индустриальное ООП недостаточно хорошо, чтобы сделать это без боли (хотя бы из-за отсутствия multiple dispatching'а), а спор о сравнительной сладости разных сортов боли — так себе занятие…
                                                                                                                              +2
                                                                                                                              Утверждения статьи очень спорные.

                                                                                                                              1. Про «не подошёл к автору». Очевидно, предполагается, что автор всегда есть и его можно спросить? Как предполагается это делать, если автор ушёл в другую фирму, уехал в другую страну, ушёл в окончательную нирвану?
                                                                                                                              Даже если он в той же фирме, он может не получить запрос на ревью; сказать, что он всё забыл; и так далее.
                                                                                                                              Подобные проблемы лечатся, очевидно, документированием. Если есть откровенно подозрительно выглядящий код, надо смотреть на комментарии в самом коде; на комментарии к ревью, если оно сохранилось; на тикет; и так далее, где как организовано. При нормальном документировании задачи «спросить автора» обычно не возникает, даже если он сидит рядом.

                                                                                                                              2. Уже сказали — прямой push без peer review это очень сомнительный вариант для современной коллективной работы.

                                                                                                                              3. Дублирование кода плохо не только потому, что кому-то религия не велит. Оно плохо по двум явным причинам:
                                                                                                                              1) Легко потерять один-два из множества одинаковых кусков, когда надо будет править.
                                                                                                                              2) Патчи могут криво наложиться.

                                                                                                                              Про (2) подробнее: у меня был случай такого рода: есть два одинаковых куска кода, для примера, со строк 100 и 200, по 30 строк. В версию 1 оно попало в таком виде, в версии 2 добавился перед этим кусок на 150 строк. Патч для версии 2 изменяет только первый из двух одинаковых кусков, со строки 250. Перенос его на версию 1 заставляет patch сделать сложный выбор — и он его делает неправильно — применяя ко второму куску (до которого 50 строк, а не 150).
                                                                                                                              При стандартном объёме контекста в 3 строки до и после — такое может произойти на любом повторяющемся куске минимум в 7 строк.
                                                                                                                              Тут автор статьи говорит про 5 строк. Может, у него бы и не случилось такого. Но сомнительно — и любая микродобавка уже выявит проблему.

                                                                                                                              Если это был бы C/C++, я бы вынес общий код в макро/лямбду, соответственно. В примере JavaScript? Вероятно, для него есть эффективные аналоги.

                                                                                                                              Но исправлять такое надо по любому. Вот если таки надо будет менять какие-то частные случаи — тогда вначале разворачивать до дублирования.
                                                                                                                                –3
                                                                                                                                но мне понадобились годы для того, чтобы понять правоту руководителя и коллеги.


                                                                                                                                Первое правило программиста: «Работает? — Не трогай!»
                                                                                                                                  +1
                                                                                                                                  С какого срока давности начинается «работает»?
                                                                                                                                    0
                                                                                                                                    Пока не доказано обратное
                                                                                                                                  0

                                                                                                                                  Крайности всегда вредны. Дао: ищи срединный путь.

                                                                                                                                      0
                                                                                                                                      Код работал.

                                                                                                                                      Тексту отчаяно не хватает истории того как этот факт был установлен и как автор поверил его сохранение.
                                                                                                                                        0
                                                                                                                                        Вот поэтому я сначала часто создаю код, как он выглядел у автора ДО оптимизации. А оптимизировать можно тогда когда заказчик уже понимает что ему вообще нужно. А без демоверсии он в 50% случаев не понимает, а в 50% случае забывает про «мелкие детали» которые меняют всё. После чего оказывается, что надо половину переписать, и после этого абстракции будут совершенно другими.
                                                                                                                                          0

                                                                                                                                          И если я работаю Аджайл — то он остается таким на всегда обычно :)
                                                                                                                                          Наблюдение из жизни.

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

                                                                                                                                        Самое читаемое