Pull to refresh

Comments 328

Всегда успеется переписать? Сомнительно.
А менеджер вообще знает, что он хлеб печет? Судя по его заказам — не знает.
часто так получается, что даже заказчики не знают, чего они хотят. чего уж говорить о менеджерах
Если нет 500 страничной документации на проект, то переписать какие-то части придется, потому что все постоянно меняется и ничего до конца не ясно, ни заказчику, ни тем более менеджеру.
Часто во многих местах пишется говнокод. А потом, переправленный на сто раз и весь в заплатках говнокод, переписывается, когда заказчик доволен всем и это оказывается именно то что он хотел.
Если она есть, то будет все еще хуже. Вы ведь ей доверитесь, а зря.
Если она подписана, то программисты и менеджеры в принципе, имеют полное положить на клиентское «а я хочу чтоб оно ещё и самолёты делало!» большой и толстый… мм… синглтон :)
Как вы его назвали… а ведь да, прямо по Фрейду…
Положить то конечно можно, но если в итоге клиент получит систему полностью соответствующую документации и всем спецификациям, но не способную функционировать должным образом, то виноват в любом случае окажется разработчик. Конечно, формально в этом случае контракт будет выполнен и никаких легальных претензий со стороны клиента быть не может, но репутация разрабочика все равно пострадает. Разочарованный клиен, как правило, никогда не признает своих ошибок и в неформальных бесдах будет негативно отзываться об исполнителе работы.
А вы уверены что исполнитель сможет реально выяснить, нет ли противоречий в ТЗ на 500 страниц перед тем как его подпишет?
Эт пока в свое резюме такие программисты не впишут очередной эпик фейл.

К слову, за 20 лет работы я ни разу не видел 100% выполнения первоначального ТЗ. Просто потому, что этот гроссбух устаревает еще до того, как на нем чернила просохнут.

И всегда эта игра в бумажки заканчивается «мы можем из этого провала вытянуть нечто сферическое в вакууме, но нам надо еще 1 год и пять лямов» (Ц), и еще очень хорошо, если количество итераций ограничивается тремя — пятью, а то ведь цифры бывают и двузначными.
Иметь-то они имеют, но это только если разработчикам пофигу, будет ли кто-нибудь использовать результат их труда, и что при этом думать. Потому как раз отсутствие 500-страничной документации — база для последующих компромиссов =)
Это называется «итальянская забастовка»
Рефакторинг в помощь…
UFO just landed and posted this here
отличная гифка) я впервые её увидел в твиттре у того самого Маркуса П.
Скорее всего в ней будет описано всё, кроме того что действительно важно. Как-то раз наблюдал подобную ситуацию.
Когда сравниваем две картинки кажется что слева всего много и все сложно.
Когда заглянем в солюшен эксплорер засомневаемся, а когда заглянем в исходный код — картинки поменяются местами.

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

Хотите быстого старта — используйте готовые фреймворки.

Архитектурное планирование необходимо. Надо придерживаться разумного балланса.
Хотите быстого старта — используйте готовые фреймворки.
ХЗ. Если эта фрамуга — не Гуава, то есть почти 146% шанс, что придётся допиливать или дообвязывать.
Чесно говоря я не знаю, что такое фрамуга и гуава, но подозреваю что если речь о быстром старте — то предполагается, что когда бизнес поднимется, данный продукт заменит новое решение. Если работа с фреймворком ведется как с блек боксом — то чем меньше будет расширений для него — тем лучше на этом этапе.
Есть один классный «паттерн», помогающий не только в программировании: «Золотая середина».

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

Проблема Маркуса — отсталость и не имение представления о том, какие вообще паттерны и средства существуют на данный момент. Он не видит перспективы и общей архитектуры проекта, а реализуют чётко поставленную задачу. Короче этот товарищ скорее малоопытен, недостаточно любознателен или просто ленив.

В любом случае, отчасти это вина проект-менеджера. Именно в его обязанности входит грамотная поставновка полной задачи, взаимодействие с заказчиком и проработка архитектуры будущего приложения совместно с разработчиком. Так что подтверждаю Ваши слова, «Архитектурное планирование необходимо». А иначе — это просто игра в песочнице, а не серьёзная работа.
Сторонники коротких итераций поглядывают на вас как-то грустно и одновременно неодобряюще.
Не путайте архитектуру и итерацию.
Короткие итерации вынуждают делать архитектуру простой, но в то же время высокоэффективной.
Я согласен, но проектировать даже простую архитектуру всё равно ведь необходимо. Независимо от того, разбиваются задачи на большие или мелкие итерации.
При таком подходе на первый план выходят не архитектурные изыски, а создание конструкторов, с помощью которых потом строится весь остальной код. Например, если стандартизировать передачу данных между модулями, зафиксировать формат и методику обработки, то это послужит той ниточкой, на которую будет нанизано «ожерелье» исполнительных блоков. Это простое как двери архитектурное решение, которое можно все так же итерационно эволюционировать, но оно не требует вдумчивого планирования всего и вся.
Хм, а стандартизацию формата передачи, протоколов обмена и интерфейсов исполнительных блоков вы не считаете проектированием? Замечу, что это более высокоуровневое проектирование (=создание архитектуры), чем паттерны.
Проектированием можно называть что угодно, даже написание метода. Мой акцент был немного на другом. Проектирование API передачи данных не требует сложных телодвижений, которые тянут за собой обязательное документирование, создание тестов, сложных схем. Тщательность проектирования этого интерфейса заведомо убыточна, потому что вы все равно не предусмотрите всего. Ну так зачем тянуть кота за хвост, если можно арихитектуру эволюционным путем развивать?

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

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

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

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

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

Хотя второй подход, бесспорно, лучше, если изначально сущностей немного. Тут же Печь, Пирожок и Кирпич, остальное — объекты-значения. А расплодили в первом подходе выше крыши
Вопрос в том, а нужна ли их ПМ-у модель? ПМ явно пляшет от «хотелок», а не от модели. Вот Маркус и рулит, тем что делает, что от него хотят. У Эванса же написана, что модель — это артефакт нужный и заказчику и программисту. Модель нужная только программисту будет только мешать, так как у заказчика полет фантазии будет сильно дальше.
модель — это артефакт нужный и заказчику и программисту.

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

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


[sarcasm]Это тот самый agile, который дает результат без скрамов, митингов, досок и прочих странных вещей с непонятными названиями. К сожалению, если использовать его именно так, то менеджеры окажутся не нужны и их уволят, поэтому большинство программистов все еще остается командой с гибкими и прогрессивными методиками разработки и нерабочим продуктом.[/sarcasm]
Когда в итоге самый опытный начинает писать код также как студент первого курса.


Потому, что знает, что отрефакторить газонокосилку в пулемёт Калашникова всегда успеет, ибо хорошо знает как это делать.
Я бы добавил, что они пишет так, как вроде может написать студент первого курса, но чисто случайно. Грубо говоря, есть с десяток возможностей написать нужную функциональность и все они примерно одинаковы по известным студенту (читай — очевидным) метрикам, но по неизвестной студенту метрике «простая возможность рефакторинга». Студент выберет случайно одну из десяти, а опытный сознательно ту самую, которую будет проще рефакторить.
сознательно ту самую, которую будет проще рефакторить.
Вот оно — дао и дзен настоящего предварительного проектирования. Это не какие-то там «дизайн-паттерны»…
Большинство проектов начинают писать вот такие Борисы, с множеством фабрик на все случаи жизни, используя все 24 паттерна. А заканчивают едакие Маркусы, после чего архитектуру не прочитает ни один вменяемый программист. И вроде как все покрыто тестами, но они уже 7 итераций как не запускались, там чтото(!) падает…

Спасибо за статью :) Качественно написано
Ошибка в том, что HeatingFactory на выходе выдает что-то такое абстрактно нагревающее (AbstractHeatingSmth), что непонятно как включать? Т.е. без известных методов?

Да, и я в этой истории на стороне Бориса, как ни странно, в его ахритектуре больше информации. Да, немного с избытком. Чтобы навести порядок в его коде — достаточно будет 30 минут вдумчивого чтения, и да — любую часть покрыть тестами, выборочно.

Чтобы проверить код Маркуса для начала необходим будет сам Маркус, причем достаточно свежий. Который сможет по номерам сказать какой тип булочки под номером 4, и какой тип печи под номером 3, потому что это не очевидно. Отрефакторить такое сложнее чем кажется. И как показывает практика — переписывать никогда не успевается. Стартапы так и гибнут, когда старый код начинает пахнуть, а ПМ хочет новых безумных фишек.
Который сможет по номерам сказать какой тип булочки под номером 4, и какой тип печи под номером 3, потому что это не очевидно.

#define BREAD_TYPE_WHITE 1
#define BREAD_TYPE_BLACK 2
#define BREAD_TYPE_SAMSA 3
#define BREAD_TYPE_KULEBYAKA 4

#define OVEN_TYPE_MICROWAVE 1
#define OVEN_TYPE_GAS 2
#define OVEN_TYPE_RUSSIAN 3

// Комментарии тоже никто не отменял
комментарии быстро устаревают, поэтому лучше константы с нормальными именами
Терпеть не могу когда пишут магическую лабуду и комментарий, вместо того чтобы просто назвать лабуду по-человечески и не надо уже никаких комментов
UFO just landed and posted this here
define, const, enum… способов избежать «магических чисел» много ;)
И в большинстве языков с недостаточно строгой типизацией (типа C), ни один из них не запретит писать бредовые и совершенно нечитаемые конструкции в духе manager.createBread(17, 42). Или, и того хуже — передать в аргумент «тип печки» значение из «вида рецепта».

И это не последняя причина для чуть большего размышления об архитектуре.

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

Всё, что нас не убивает, делает нас сильнее, угу.
Да, тип «кирпич» вводить было не нужно. Это действительно отдельный тип хлеба, и ветвление в коде пойдет довольно глубоко, если пойдет вообще — хорошо написанный метод разберется с ситуацией, ориентируясь только на физикохимические параметры сырья (неважно, теста или глины) и желаемого результата. А смотреть на то, хлеб нужен, или кирпич, без необходимости — зачем? Keep it simple!
и пусть себе желают шамотную булочку, надо — испечём…
Боевой гномий хлеб
Поясните, пожалуйста, для маркусов, чем ваша конструкция лучше чем
enum BreadType { Foo=1, Bar=2 }
?
потому что у вас будет
BreadType bt = Foo;
а у valashko
BreadType::Type bt = BreadType::Foo;

а на самом деле нужно использовать C++11:
enum class BreadType { Foo=1, Bar=2 }
и дальше
BreadType bt = BreadType::Foo;
UFO just landed and posted this here
Неймспейс не засоряется. Иначе Foo и Bar будут видны без префикса BreadType. Разумеется, в данном случае речь только про C/C++.
Напомните, а как в C (который у вас первый в паре C/C++) создать неймспейс и, по возможности, засорить его?
UFO just landed and posted this here
К сожалению, это не C.
Это очень просто. Достаточно представить на секунду, что подразумевается не буквальный «неймспейс», как объявленный юзером scope-контейнер. После чего, обратиться за подтверждением своей теории к ISO.IEC-9899.1999.pdf:

6.2.3 Name spaces of identifiers
If more than one declaration of a particular identifier is visible at any point in a translation unit, the syntactic context disambiguates uses that refer to different entities.
Thus, there are separatename spacesfor various categories of identifiers, as follows:
— label names(disambiguated by the syntax of the label declaration and use);
— thetags of structures, unions, and enumerations (disambiguated by following any of the keywords struct, union, or enum);
— themembersof structures or unions; each structure or union has a separate name
space for its members (disambiguated by the type of the expression used to access the
member via the.or->operator);
— all other identifiers, calledordinary identifiers(declared in ordinary declarators or as
enumeration constants).
Пожалуйста, уточните, какую именно теорию в данный момент вы подтверждаете этой цитатой? Неймспейсы, как пространства имен, а не как области видимости, перечисленные в этой цитате, не могут быть созданы по воле программиста, ибо являются частью самого языка.
Где в моём исходном комментарии я говорил про создание _своего_ пространства имён, в C, или C++?

> Неймспейсы, как пространства имен, а не как области видимости
Ага, т.е. «неймспейс» можно прочитать и так и так. Я подразумевал «область видимости», другие прочитали «кастомное пространство имён». Энд оф стори?
Области видимости создать можно и в C, и в С++, не прилагая для этого никаких усилий (разве что фигурные скобки напечатать). Тогда как простраства имен в C создать нельзя, нет для этого соответствующих механизмов.

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

Энд оф стори.
Энд оф стори, да.

Но вот это интересно:
> Мой изначальный комментарий ставил перед собой целью предупредить дальнейшее использование названия языка «С» в качестве синонима для «С++»
Меня? Других? Во-первых, я прекрасно знаю разницу. Во-вторых, написал я их вместе (через слэш) _намеренно_, потому что сабжевая проблема встречается в обоих языках. Ошибка на ошибке: о том что в C якобы можно создавать неймспейсы и что некто по ту сторону не знает разницы между C и C++.

Окей, хотелось как лучше. Но всем известно куда вымощена дорога с такими благими намерениями. Возможно виновата подача, но у меня на протяжении всей ветки обсуждения перед глазами стоял очередной воин, вызывая раздражающие, болевые позывы в одном месте.
UFO just landed and posted this here
тогда вопрос в догонку:
почему namespace сделан через struct, а не через namespace?
Наследие С… Но чаще пишут так
class Bread
{
public:
    enum Type { ... };

    ...
};
UFO just landed and posted this here
Можно еще так:

namespace BreadType {
    enum {
        White = 1,
        Black // etc
    };
}
Подход Маркуса не исключает комментариев в коде с указанием номеров булочек и печей.
А если это отдельная библиотека?
А как вы собираетесь с ней работать если к ней нет описания API?
стартапы гибнут по другому. Переписать код, зная как работает старый и перенести данные со старого движка в новый — не так сложно — в старом уже все учтено и можно написать четкое ТЗ ко второй итерации (а первому программисту никто ТЗ не давал- так и бегали «добавь кирпичей» и «концепция изменилась — заказчик хочет пылесос»). При этом второй раз можно уже и по Борисовому написать и на другом языке(времена меняются). Маркус стартует стартап, стартапу выделяются деньги на которые десяток Борисов пишут чистый новый код. А писать на начальном этапе Борисов код, когда не понятно еще ни ТЗ ни выстреливаемость стартапа — не целесообразно. Писать поддерживаемый код нужно тогда, когда есть понимание что его нужно будет поддерживать, а 90% стартапов дохнет. Так какой смысл?
Часто успешный проект доходит до стадии взрывного роста новый хотелок при скромном росте дохода с него. Тогда просто нет денег чтобы сесть и переписать всё.
В общем-то, хотелки, не подтверждённые деньгами, — это не хотелки, а так себе…
Вы сейчас только про заказные проекты говорите.
>а 90% стартапов дохнет.

Так, может, это вносит свою лепту в 90%?
99% биологических видов вымерло… тут то же, но менее затратно.
Переписать код, зная как работает старый и перенести данные со старого движка в новый — не так сложно — в старом уже все учтено и можно написать четкое ТЗ ко второй итерации

Ключевое слово «зная». А в ситуации когда программист первой итерации уволился, а у менеджера остались максимум записи того, что он говорил на первой стадии, а то и этого нет, а есть продукт у которого есть исходники и который вроде текущим требованиям удовлетворяет, я бы предпочёл чтобы мне в наследство достался код Бориса, а не Маркуса.
Обычно такие вещи (соответствие типов с названиями и описанием) просто хранят во внешнем справочнике (в маленькой базульке mysql например, там можно и редактирование прикрутить и интерфейс простенький для менеджера. Ну или в xml или на худой конец csv)

Использовать цифровые типа без описаний конечно не дальновидно
Имхо, БД — это ещё хуже для поддержки. Если хардкодить, то хотя бы соответствие номера и названия будет где-то в коде, если же хранить его в БД, то при изучении кода надо будет лезть в БД, чтобы узнать соответствие. Как правило, это более долгая операция, чем открытие файла в редакторе, не говоря о функциях IDE типа Find usages. Ну и хардкод позволит избежать рассинхронизации продакшен и дев окружения. Менеджер прибежит с криком «Булочка обжигается, а должна печься» и разработчику придётся лезть на продакшен базу, чтобы понять что там нынче менеджеры булочками обозвали. Ещё хуже, если он начнёт исходить из того, что у булочки ид 1, а на самом деле менеджер имел в виду запись с ид 101.

Имхо, если и выносить подобные вещи в базу, то использовать не суррогатные числовые ид, а текстовые. что бы вместо кода типа if (bread.type == 1) был код типа if (bread.type == 'black'), а суррогатные использовать только для связи между таблицами.
Я тут писал длинный коммент, который в итоге стер потому что его можно просто написать одной строкой — всё зависит от конкретной ситуации.
Ага, а вы видели код этого Manager? Который знает все о рецептах, типах печей, управлении газом.
да. он чудовищен. но имхо его можно отрефакторить
Нет, отрефакторить его нельзя, потому что для этого нужно покрытие тестами, о чем Маркус явно не в курсе.

Весь код надо будет переписывать, как у Бориса (хотя тут видно что Борис — тоже не супер спец, так много сущностей плодить не обязательно было сразу).
К коду Маркуса гораздо легче написать функциональные тесты. А дальше можно спокойно рефакторить.
UFO just landed and posted this here
А и незачем. Если хлеб и пирожки пекутся — то что еще нужно от этого кода?
Да, но путём тестов можно узнать, что получившаяся система ещё может и сталь выплавлять!
Вырефакторивать в отдельный метод каждый костыль.
Мы вырефакторивали-вырефакторировали, да не доревыфакторировали.
Почему недовырефакторивали? Человеко-часов не дали больше?
не соглашусь. там у объектов есть состояния, из-за этого тесты крайне сложно писать
Я рефакторю без тестов, доверяю IDE.
Рефакторинг — это не только переименование, %username%
IDE может не только переименовывать, но и, например, выделять метод или функцию, %username%
IDE еще умеет разделять классы, выделять интерфейсы. А еще оно НЕ умеет кучу других вещей которые нужны при рефакторинге.
Вопрос времени, имхо, если говорить о рефакторинге в узком смысле слова. Вообще, конечно, совсем без тестов сложно, но, с другой стороны, если есть старый код, который разрабатывался без учёта возможности тестирования, то нормальная IDE хотя бы с тем же выделением методов/функций очень может помочь в деле покрытия тестами для рефакторинга в широком смысле слова.

Вот представьте, есть у вас «плоский» скрипт на PHP на тысячу-другую строк, который вызывается веб-сервером, или программа на Си из одной функции main(), или программа на Java из одного класса с одним методом main. Для покрытия его тестами без рефакторинга придётся, как минимум, эмулировать веб-сервер, а с помощью IDE мы можем безопасно (ну, считая, что разработчики IDE не допустили ошибок) разорвать зависимости логики от получения параметров программы из окружения, чтобы иметь возможность эмулировать окружение в среде тестирования и тестировать логику, а не создавать виртуальное окружение и тестировать всю программу целиком.

Если всё равно не понятно о чём я, могу привести чуть позже примеры на PHP.
Смотря какой рефакторинг. Мне недавно пришлось переколбасить ядро своего фреймворка, чтобы ввести поддержку новых требований. Диаграмма сервисных классов изменилась до неузнаваемости, при полном сохранении контрактов существующих интерфейсов. Без тестов все бы рухнуло.
Реально падали тесты и вы потом исправляли?
Да, рефакторинг выходил за пределы возможностей IDE. Например была одна структура, которая умела себя строить по определенному образцу. Мне нужно было сделать ее более пассивной — только для хранения данных, а построение доверить классу сборщику. Тесты у меня довольно высокоуровневые — тестируют в основном контракты высокоуровневых интрефейсов, которые не менялись, переписывать их почти не пришлось, но пара тестов свалилась из за ошибок при переносе.
Плюс еще в одном месте пришлось переписать MergeSort на QuickSort — вроде рефакторинг, с сохранением внешного контракта, а IDE ничем помочь не может. Тут тоже тест помог отловить баги.
Покрытие тестами — это хорошо, но всегда ли необходимо?
Поясню. Если вы делаете что-то мелкое, то вряд ли вы будете использовать методы Бориса. Оно мелкое, его результаты легко проверить на практике, а отладку делать при помощи нескольких брякпоинтов.
Как вариант — написание чего-то сильно критичного к скорости работы. Надеюсь, вы не будете отрицать, что при прочих равных вариант Маркуса будет работать быстрее варианта Бориса?
Ну а нагромождение классов и тесты стоит использовать там, где нужна надежность и стабильность работы.
Я к чему все это… Не стоит забивать микроскопом гвозди, а на бактерии смотреть сквозь лупу.
Если вы на 100% уверены, что делаете что-то мелкое и, самое главное, оно таковым и останется, то конечно используйте метод Маркуса.

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

А вот покрытие тестами мне жизнь хуже еще ни разу не сделало.
Методом всасывания и упразднение посредников можно отрефакторить и Борисовы художества. Другое дело, что выделить суперкласс раз в 10 легче, чем упразднить…
И тогда получится то, что на картинке слева.
Я видела.
Есть у нас продукт, а точнее два файла один на 40к строк кода, второй на 60к строк и это только .aspx.cs, сама вёрстка в два раза больше. Дак вот, это и есть тот случай. Понять, что там происходит — просто не реально.
Спасибо, это надо сохранить.
Мне кажется метод Маркус очень похож на создания прототипа, что в данном конкретном случае конечно лучше чем подход Бориса. Потом главное этот прототип отрефакторить знаю уже большенство требований заказчика, которые были скрыты в начале разработки.

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

Я не думаю, что у Маркуса все так запущено, даже студент догадается сделать тут enum.
Если делаешь прототип, не забудь его выбросить.
Полностью согласен про прототипы и очень удивлён, что об этом практически ничего не написали в комментариях. Вся эта история высосана из пальца и предоставляет 2 крайности совершенно не опытных программистов, которые могут легко исправить положения одним тим лидом, если их проект-менеджер занят книжками «я познаю мир» :)
Вообще в большинстве случаев при разработке любого проекта, аналогов которого нет, то есть реально новый функционал, которые необходим заказчику, а тот в свою очередь если не программист никак не может продумать всей архитектуры, а особенно объяснить все детали проект-менеджеру. В таких случаях всегда разрабатывается прототип и подгоняется его работа под требования без всяких свистелок и прочей мешуры, должно просто выполнять основные задачи для работы данного проекта. И только потом на основе этого прототипа создаётся архитектура и всё это обвешивается классами и рефакторится 10 раз. Все выводы из собственного опыта автоматизации разного рода бизнеса, когда понять задачу было просто очень сложно понять изначально всех мелочей каждого бизнес-процесса, всё это только в процессе предварительного тестирования и запуска прототипа возможно выявить.
«Не плоди сущности сверх необходимого» Окама.
Основная проблема Бориса в том, что он сразу стал генерализировать и обобщать сущности, порождая при этом новые сверх необходимости. В этом его основная ошибка. Кроме этого, сущности должны отличатся поведением.
Ну а у Марка конечно, будет спагетти внутри, что конечно же скажется на сопровождении.
В общем, это две крайности, которых следует избегать.
До меня долетел другой посыл: четко определись что именно нужно делать, прежде чем делать.
>> А переписать всё заново, если что, — это всегда успеется.
Вот с переписать всегда проблемы. Если проект на 1к строк, то конечно, можно и переписать, а когда там уже 1кк… такое уже не переписывается, а добавлется очередной адъ внутрь реализаций. А еще есть такая штука, как обратная совместимость, которая не позволит просто так взять и добавить еще один параметр в метод функции. Вот что тогда будет делать Маркус?
упс… не в ту ветку запостилось :'(
Согласен — две крайности. Они тут полезны в том что мы хотим понять. Я считаю что оба программиста молодцы, а вот косячник — менеджер.
Программисты вообще всегда молодцы, живут в своем мирке и как знают мирок свой так и пишут.
А вот менеджеры, блин, ну неужели они не могу объяснить какой будет проект:
1) прототип для показа — который выкинут и надо код писать быстрее пусть с утечками памяти немасштабируемый и т.д. (Маркус тут даже перестарался, все можно было написать в main()),
2) серезный проект с ТЗ, которого действительно утверждено — надо показать это ТЗ, диаграмки состояний нарисовать, объяснить как в дальнейшем может развиться система. Какие перспективы сопровождения (Борис не виноват что ему не предоставили полной информации, однако он правильно начал с минимумом абстракций, а потом их наращивал, основной бедой его был повар, который является инициатором выпечки — он фабрика, а не плита, плита — инструмент, который на ровне с рецептом должен использоваться поваром и просто поддерживать температуру определенную, из-за неправильного повора рецепт вперся в плиту зачем-то и пошло поехало, Борис действительно не мог остановиться и развернуть архитектуру вовремя из-за косноязычности менеджера, кирпичи выпекать стало не кому выходит, кстати хороший паттерн чтобы не плодить под пирожки своих поворов — мост / Bridge)
3) стартап, в котором надо делать быстро и с возможность масштабирования. Тут любой подход хорош, единтсвенное надо реализовывать его качественно. Если разобратся в подходах данных программистов, то Борис пишит объектно-ориентированный код, а Маркус — процедурно-ориентированный, ну и кто сказал что последний сложно тестировать? Можно и нужно поколоть его основные методы на составляющие и их так же легко тестировать как объекты. Разница их подходов лишь в том, что у Бориса есть модель объектов, она более наглядна, чем математическая модель Маркуса. А код один и тот же, просто дайте процедурнику объектно ориентированный код, он его поймет, но напишит все посвоему и криво. А если наоборот, то объектник начнет все рефакторить излишни пытаясь структурировать. Так что лучше не мешать программистам, а грамотно менеджерам ставить задачки))

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

Бывало разное — раз заказчик сказал, что будет прототип, а потом перепишем. Написали, а его жаба заела переписывать — итак работает, мы конечно договаривались, но запилите пока на том что есть ещё пару мелких фишечек. Потом ещё фишечек. Так прошло 2 года, на проекте 2 раза сменилась команда, ну в общем вы поняли…

А что нам, команде, было делать? Тут всё зависит от Product Owner-а. Но это я про мелкий и средний конвеерный аутсорсинг, конечно в продукте всё по-другому.
Знаете, у меня тут полно кода Маркуса. Его действительно круто фиксить итд, но чьорт побери! Листинги функции на 5 экранов и отсутствие тестов при портировании на другую архитектуру… ААААААА!!!!111
Написано красиво, не без смысла, но наблюдая оба подхода, поддерживая и развивая проекты, доставшиеся по наследству, мне кажется, что главное, это не делать все слишкмо гибко или слишком просто, а делать это понятным. Если у Маркуса понятный код и прозрачная, однообразная логика — все круто, если это не так — его «простую» конструкцию рано или поздно выкинут и напишут заново, потому что никто не будет понимать, как оно вообще работает.
У Бориса получился ад, т.к. очень сложно проследить взаимодействие между всеми этими классами. И это обусловленно тем, что он всегда пытается сделать через чур универсальное решение. Зато его код легко покрывается юнит тестами и соотвественно не ломается непредсказуемо сразу в 10 местах. И потенциально это дает возжность третить меньше времени на разработку — программинг, отладку, тестирование.

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

KISS здравый принцип, но в данном примере им никто не воспользовался.
кажется, главный месседж статьи в том, что Борис свой адъ не осилил.
Вы так про тесты говорите, как будто это самоцель.
Юнит-тестами там можно покрыть все, да. Только тестироваться будет присвоение переменных в конструкторах, т.к. при таком накале ООП там тестировать больше бывает и нечего. И то, что тесты выполняются, ничего абсолютно не гарантирует: это всё просто поломается не в классах, а по швам между классами.
да, я этими статьями вдохновлялся) ещё кстати в дополнение к этому у меня юмореска есть
Ваш Маркус создал God Object — это тоже плохо
принцип KISS — «сохрани это простым», а не «сохрани это тупым»,
т.е. не стоит плодить сущностей более чем необходимо, но и менее чем необходимо тоже не стоит.
> Но с помощью шаблона «шаблонный метод» беспорядок вполне можно структурировать.

Ага, и в итоге все это сведется к схеме похожей на первую :) Плюс вы бы подрисовывали квадратики «переписанного почти с нуля кода». А то складывается впечатление, что во втором подходе раз — и сразу все написано.

А схемы Бориса надо было рисовать в «проекции 5D на 2D» чтобы вообще все запутано выглядело (намек на то, что несколько выровняв их можно даже якобы кашу классов сделать понятнее).

Хотя одно верное наблюдение есть:

> Борис уже начинает что-то такое чувствовать, но остановиться уже не может.

Надо знать где остановиться с абстракцией. :) А то можно сочинять и дальше — ведь повар тоже не из вакуума взялся… мама, папа, учителя… :)
UFO just landed and posted this here
Хороший небольшой учебник по шаблонам можно написать по левой модели.
А в чём суть статьи то? Ну описали вы два подхода, которые применяются к одному заданию, но ведь важна цель задания!
Нам нужен Proof of Concept и менеджер идёт к Маркусу. Быстро, эффективно. Концеция прошла демо презентацию и то же задание получит Борис. Вот теперь у компании есть продукт.

Дело в том, что многое зависит от компании и специализации. Компания со своим продуктом уделяет большее внимание проблемам поддержки/правки кода и масштабируемости. Пример из реальной жизни — через год приходит менеджер и говорит: «Помнишь хлебо-печку? Одна большая компания хочет построить хлебзавод и мы уже продали им наш модуль!». А потом менеджер будет приходить ещё много раз с новыми требованиями для API к уже завершённому проекту… И тут вот вам захочется сломать что-нибудь очередному Маркусу. Причём что то жизнено-важное.
Любую проблему можно решить добавлением еще одного уровня абстракции. Кроме проблемы слишком большого числа уровней абстракции. =)
Принцип KISS гласит: делай настолько просто, чтобы проще было некуда.
Вопрос в том, что такое «проще». Вместо классов использовать дополнительные поля и ветвить логику? Или, наоборот, использовать полиморфизм? Однозначного ответа тут нет.
Зато есть хороший принцип третьего дублирования: если что-то хочешь продублировать в третий раз — пора с условий перейти на полиморфизм.
А заранее создавать абстрактные фабрики, отвязывание по интерфейсам и инъекции — это, естественно, усложнение, а не упрощение. А главная причина создания ООП как раз была в упрощении.
Так что, люди, используем исконные принципы ООП, а паттерны, архитектуры и т.д. — только если они упрощают жизнь.

P.S. IMHO, оба приведенных в статье примера сложны. Первый — чрезмерной абстрактностью и гибкостью, второй — слишком большим количеством условий и логики на нетипизированных данных, собранным в одном месте. Истина где-то между.
Как всегда — истина где-то посередине между двумя крайностями
А!!! Это моя любимая ситуация! Когда мы видим, что выдуманным примером, можно доказать какие угодно положения.
А если принять за факт, что 2+2=5, то можно доказать, что я — английская королева.
Доказательство — в студию! :-) мне всегда было интересно на это посмотреть )
Ну, например, так:
Доказывать будем от противного. Предположим, что я — не английская королева. Пусть n — число английских королев, которыми я являюсь. В нашем предположении n=0. Тогда:
n+(2+2)=0+5 (сложили два равенства)
(n+2)+2=5
n+2=5-2=3
n=3-2=1.
Откуда (по определению n) следует, что есть хотя бы одна английская королева, которой я являюсь. А значит, я и есть эта самая королева. Впрочем, все остальные королевы — тоже я.
P.S. Короны не жмут.
если мы предполагаем, что n=0, то тогда самое первое равенство записано не верно. n+(2+2)=0+5
Т.е. мы изначально соглашаемся с тем, что 2+2=5, что позволяет мне переписать последнее равенство в n=3-2=0
или так:
Примем, что 2+2=5.
Отнимаем от обеих частей 2: 2 = 3
Отнимаем от обеих частей 1: 1 = 2

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

Вот так выглядит создание хлеба с использованием кода Бориса:

Cook* cookchief = CookFactory.GetNeededCook(BossSay);

OvenParams ovenParams = CreateOvenParams(GasIsAvailable(), MicrowaveOvenAvailable());

AbstractOven* oven = OvenFactory.CreateOvenFromParams(ovenParams);

Product* p = cookchief->cook(oven);

return p;


А вот так — с использованием кода Маркуса:

if (BossSay("We need to cook bread!"))
{
	if (GasIsAvailable())
	{
		
		Manager.gasLevel = GetGasLevel();
		Bread* bread = Manager.createBread(OT_GAS_OVEN, GetNeededBread(), STANDARD_RECEIPT);
		return bread;
	}
	else
	{
		Manager.gasLevel = 0;
		if (MicrowaveOvenAvailable())
		{
			Bread* bread = Manager.createBread(OT_MICROWAVE_OVEN, GetNeededBread(), FAST_RECEIPT);
			return bread;
		}
		else
		{
			int neededBread = GetNeededBread();
			if (VasyaWantsBreadToo())
			{
				neededBread = neededBread + 1;
			}
			
			Bread* bread = Manager.createBread(OT_HEAT_OVEN, neededBread, STANDARD_RECEIPT);
			return bread;
		}
	}
}
else if (BossSay("We need to cook bricks!"))
{
....
}
Совсем не обязательно, у Маркуса может быть так:
if (BossSay("We need to cook bread!"))
{
    int ovenType = GetAvailableOvenType();
    Manager.gasLevel = GasIsAvailable() ? GetGasLevel() : 0;
    Bread* bread = Manager.createBread(ovenType, GetNeededBread(), ovenType  == OT_MICROWAVE_OVEN ? FAST_RECEIPT : STANDARD_RECEIPT);
    return bread;
}
else if (BossSay("We need to cook bricks!"))
{
....
}
Не поверите, но второй листинг читабельнее и его легко чуть подрефакторить, чтобы убрать громоздкость в ветвлениях. Например, вынести код в ветвлениях в отдельные приватные методы и будет вообще зашибись )
Да, С++ — это ещё дополнительный уровень хардкора…
С++ открывает ящик Пандоры.
Но никто не заставляет Вас в него заглядывать, верно?
Заглядывают в такие места обычно по любопытству и по незнанию. Заставлять не требуется.
Хм, вот интересено было бы увидеть правильный вариант слева, так как там перегиб, хотелось бы увидеть золотую середину.
Похоже на жизнь, но есть некоторый перекос. По сути проблема Бориса не в том, что он использует ООП, а в том, что не придерживается YAGNI. Если бы он не фантазировал, то его более типизированный и структурированный пример мог бы получиться даже проще, чем у его коллеги.
Не хватает ещё одного пункта: теперь менеджер просит 5 членов команды добавить по 2 печи и по 4 рецепта.
Слева картинка разрастается, справа нет, но тут какой-то подвох.
Самое интересное, как всегда, происходит вдали от компьютеров. А именно: менеджер впервые после начала разработки встречается с заказчиком и наконец-то понимает, зачем тому нужна была печка. Он (менеджер) в седьмой раз приходит к программистам и говорит:
— Нам нужно, чтобы в печи можно было обжигать кирпичи.
После этого надо было начать проект с нуля и сделать просто печку для кирпичей.

Ну а если проанализировать работу Бориса, то на мой взгляд он сделал две серьезные ошибки:
1. Неоправданно ввел набор сущностей Cook. Можно было сделать просто шаблонный метод в печке. Либо допилить печку до Фабрики Продуктов. Впрочем тут есть масса вариантов.
2. Перемудрил с печкой для кирпичей — тут варианта два, либо сделать кирпич таким же продуктом как и хлеб, либо вообще отдельную несвязанную печку для кирпичей сделать и отдельный кирпич не связанный с хлебом, т.к. предметная область очевидно иная и с развитием проекта расхождение требований будет только усугубляться.

простите что-то напутал с цитатами
UFO just landed and posted this here
«Что я хотел этим сказать? Мораль сей басни такова, что с помощью надуманного примера можно доказать всё, что угодно.» (с) Joel Spolsky
А в каком контексте он это использовал? Можете дать ссылку?
Абстракция позволяет легко вносить архитектурные изменения, а разбиение на небольшие методы/классы — обойтись без копипаста.

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

А вывод из статьи, подобной этой зависит от того, что гипертрафировать — засовывание всё в один метод или разбиение на сотни классов
Да, три года назад меня выдрючили конкретно, заявляя, что метод жолжен быть на экран…
ХЗ что у Марка внутри класса, столько приватных незаинтерфейсенных методов…
Мне кажется, что основная проблема тут не в программистках, а в посреднике между программистами и непосредственно заказчиком. В обоих случаях получился адов говнокод. В одном случае слишком общий, в другом случае слишком костыльный. Вместо того что бы сесть с заказчиком и выпытать у него все, что нужно и написать сбалансированный код, товарищей посадили и сказали «пилите, шура, пилите». Ну оба и начали пилить так, как им казалось наиболее логично в данном конкретном случае. «Нужно делать на столько просто, на сколько возможно. Но не проще.». ИМХО тут это условие не соблюлось ни одним из программистов.
Т.е. чем больше звеньев в цепи, тем больше погрешность в конечном итоге, тем больше итераций нужно что бы достичь того, что нужно. Пример: заказчик на 60% объяснил, что он хочет, ПМ понял его на 70%. Программист понял на 70% ПМа. В результате на выходе 1-ой итерации получаем: 29% от того, что заказчику действительно нужно. Испорченный телефон.
Как правило получается всё же больше 29%, т. к. программист может по опыту (своему или вычитанному в книжках) восстановить и часть из 30% непонятого ПМом, и часть из 40% невысказанного заказчиком. Например, на свой страх и риск отделить данные и логику от представления, потому что по опыту знает, что чаще приходится менять что-то одно. Или как в посте заранее создать абстрактный Product.
Ну и ПМ иногда проявляет сообразительность на уровне «либо встречу, либо нет динозавра» и угадывает неозвученные хотелки заказчика :)
Далеко не факт. Представьте, что у заказчика быстро развивающийся бизнес (во многом благодаря нашему софту :) ). На момент первой итерации объективно не было никаких других требований кроме «чтоб пекся хлеб», заказчик пек хлеб на аутсорсе :). На момент второй итерации купил/арендовал собственно печь, чтобы сократить издержки, затем покупатели начали намекать, что одного сорта хлеба им мало, а затем внезапно женился на дочери мэра и получил контракт на поставку кирпичей :)

Что в подобных ситуациях может сделать хоть менеджер, хоть заказчик?
Вывод какой? Из-за того, что менеджер не сумел с заказчиком сразу проработать нормальный ТЗ и не сумел сам определиться с целями и этапами проекта (а так же не сумел донести их до разработчиков), оба разработчика написали говнокод. Каждый — в своём стиле. С чем я этого менеджера и поздравляю.
Точно. И Маркус уже прошарил, что этот менеджер некомпетентен. Можно даже сказать, что скорее всего его вырастил именно такой менеджер. Который требует очень быстро всё решать, когда ниче не известо, когда всё меняется по сто раз на дню… А Борис воспитывался в нормальной обстановке =D.

Вывод: Борису надо срочно валить из этой конторы, пока он не превратился в Маркуса. Маркус будет работать в этой конторе, а Борис вскоре уволится, если не превратится в Маркуса.
Борис в итоге стОит дороже ибо требует более адекватного менеджера.
Ага. Я об этом и говорил
Борису надо срочно валить из этой конторы, пока он не превратился в Маркуса.
Не можете объяснить, почему именно в такой формулировке — «срочно, пока не превратился»? Есть опасность, что вскоре превратится, и это плохо?
А если объективно не было известно, что заказчик будет заниматься обжигом кирпичей?
Такой вариант тоже не исключен. Но тогда выглядит всё так: заказчик захотел печь хлеб, ему всё сделали, проект сдали. Потом через 2 года заказчик расширяется и собирается осваивать обжиг кирпичей. Начинается весёлое кино, где сотрудники хлебопекарного отдела начинают наезжать на строителей, строители — на бухгалтеров, бухгалтеры — на менеджеров по продажам, а менеджеры — на всех остальных. При этом задача автоматизации нового бизнес-процесса стоит аж на десятом месте по геморроидальности после всего остального. Вот в этих условиях уровень говнокодистости зависит на 99% от ряда причин, которые к программистам имеют мало отношения. Конечно, если есть legacy система, которая развивалась лет 15, причём разными людьми, в разное время, на разных языках, то всё будет выглядеть печально, это неизбежная штука.

Из примера автора понятно следующее. У заказчика возникло желание автоматизировать бизнес (или создать новый бизнес, или создать новую систему), при этом он не мог внятно донести, чего он хочет (его право, заказчик не обязан понимать, чего он хочет :-) ). Как поступил PM: ему поступала всё новая информация от заказчика, он тут же делал «гав», и, скорее всего, прямо-таки прямым текстом кидал эту новую информацию. Это не объективно, это лишь свидетельство плохой организации работы.
А я один вижу на картинке для привлечения внимания фалический символ?
теперь уже нет. зачем вы это написали?)
Это вы просто не проголодались ещё.
PS: два «л» в слове.
Левая картинка очень напоминает проект, который приходиться сапортить. Спасибо за пост.
Правая картинка очень напоминает проект, который приходится переписывать: на десятки тысяч SLOC всего пять нормальных классов, лапша на десять экранов, вложенность блоков вынуждает использовать горизонтальную прокрутку, сплошные side effects, чинишь в одном, ломается в другом. Спасибо за пост.
Искусство программирования и получения удовольствия от процесса против говнокодерства для быстрого результата.
А заказчики-то думают, что цель — всё-таки результат :)
Я бы не назвал левый вариант искусством. Скорее графоманией или «искусством фотографии» в стиле «сделаю 100500 снимков, хоть один да получится нормальный».
Интересная статья, как раз думал с какой стороны за проект взяться. Либо просто сделать или извращаться с кучей классов.
Беритесь с любого конца, но придите к балансу гибкости и простоты. Имхо, оба примера не примеры для подражания. А субъективно второй проще привести к балансу (если сам код одинакового качества).
Спасибо за очередное анти-строгое-ооп. Солидарен, неоднократно в комментариях высказывался по этому поводу.
Чтобы было проще, надо убрать ненужные вещи:
— убираем заказчика
— убираем манагера.

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

Мысль автора ясна и в ней есть доля правды, но меня больше интересует другой вопрос: «А нужен ли UML?».

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

Хотелось бы услышать ваше мнение на этот счет?
Я пользуюсь уже после того, как код написан. Только для документации. Если программист на столько туп, что ему в тз нужно UML рисовать — в большинстве случаев он не сможет прочитать эту диаграмму, и притопает с вопросами.
Я им пользусь только потому, что в документации классы хорошо выглядят и получаются хорошие рисунки.
Нужен. За тем же, за чем опытному программисту нужен опыт написания систем в стиле Бориса.
практика показала, что нужен, но совсем не так часто, и не так много, как об этом говорят. в данном случае юмл просто иллюстрирует, что пишут программисты. чтоб нагляднее было, и не пришлось привязываться к одному языку.

проектирование в юмл оправдано только тогда, когда естьт обратная связь из кода в юмл

Это, кстати, неправда. Юмл вообще не показывает то, что написал Маркус

Я написал несколько курсовиков про UML, учавствовал в доработке редакторов и кодогенераторов. И затем защитил диплом на тему создания редактора, на котором можно нарисовать сам UML :)
UML — не нужен.

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

Я не пользуюсь формальными нотациями UML («забыл, да толком и не знал»), но схемы, которые рисую на бумажке, имеют много с ними общего.
Все же решил сделать отдельный опрос здесь и прояснить, как обстоит дело в реальности.
Смотря какие диаграммы рисовать. Диаграммы классов — в топку. Просто набирать эти же сущности текстом — быстрее (и удобнее, особенно если это domain model или IoC или еще какая бяка в аннотациях а-ля hibernate). И в чем тогда смысл вошканья мышкой, если без него — в разы быстрее?

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

А вот состояния, flow, по времени и т.п. — весьма неплохи, когда надо проиллюстрировать тонкие / важные места.
имхо UML нужен в зависимости от ситуации. Если по проекту удалось сделать подробное ТЗ и требования фиксированы, то UML и высокоуровневое ООП (шаблоны, паттерны) сильно поможет в реализации. Если же требования неясны или изменяются в ходе работ, проектирование не подходит и нужен итерационный подход, может использоваться процедурное программирование или функциональный язык.

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

  • PMBoK — это «стиль» Бориса в переложении на управление проектами. Менеджеру, соответственно, понятнее и ближе.
  • Сокращать иерархию классов сложнее (сам делал недавно «всасывание» кода с последующим упразднением классов, из которых он «всасывался»), чем усложнять — тут автоматический рефакторинг поменял на 180 градусов предстваления об обратимом и необратимом.
  • Если на задачу, которую можно сделать «в одну харю», назначить троих человек, есть очень большой шанс не уложиться в сроки.
  • Решение задачи усилиями в разы меньше запланированных есть проявление неуважения к менеджменту путём выставление его дураками (моего приятеля за это 10 лет назад уволили). Сложные структуры, написанные Борисом, лучше отражают масштабность и объем задач и больше тешат самолюбие менеджера.
Плохая статья. Пример поведения Бориса притянут за уши — эдакое гипертрофированное ООПроектирование. Маркус так вообще не умеет пользоваться ООП, ему лучше в структурной парадигме работать. Налицо неверное применение методик проектирования (!), а не программирования. Отсюда и последствия. Следовательно, сделанные выводы корректны только для этого некорректного примера. KISS KISS'ом, но сравнивать извращенное применение методик это нелепо.
Дата рождения: 14 февраля 2150

да, когда вы родитесь, всё будет по-другому
Серёга, ты распиши в чем некорректность применения методик. Было бы познавательно.
А истина как всегда где-то между.
Самая большая проблема, это вовремя перейти от стиля Маркуса к стилю Бориса. Если перейти слишком рано, когда конечная цель не видна — будет странная архитектура, а если слишком поздно, когда рефакторинг лапши становится слишком трудоемким, то архитектуры не будет совсем.
По-моему вообще какая-то сферическая статья в вакууме, одна вода.
Ох. Это я не глядя вместо корня в ветку…

А статья да, очень «теоретическая», но ставит правильные вопросы.
Небольшой вывод — применение любых методологий просто ради их применения в легкую может привести совсем не туда, куда ожидалось. Любые методологии, шаблоны и прочий Фаулер придуманы для того, чтобы решать конкретные задачи. Но есть большой соблазн вляпаться в них до появления самих задач. Вот и получается такая, с позволения сказать, хлебопечка.
Подход Маркуса, кончено, не позволяет использовать модульное тестирование, но зато он даёт результат намного быстрее

То есть и тесты тоже не нужны? Ок, удачи )
Подход Маркуса позволяет писать функциональные тесты, этого вполне достаточно.
Для всех случаев жизни? Мы же понимаем, что данный пример — абстракция?
Зачем же для всех? Функциональными тестами можно и нужно покрывать наиболее распространенные варианты использования. Но все — незачем.
При чём тут варианты использования абстрактного примера про печку? Я говорю про решение реальных задач. Юнит-тесты взять и отменить? Писать всё в одном классе; зачем нам наследование, если можно просто добавить целочисленный код и пачку if/switch; ну и т.д.? В общем, вам тоже удачи )
1. А я и не говорил про печку.
2. Я считаю, что функциональные тесты полезнее юнит-тестов (но не отменяют их). Особенно в плане рефакторинга.
1. А каким образом вы пришли от «для всех случаев жизни» к «вариантам использования»? Ваша логика от меня ускользнула.
2. Каким образом вы заключили, что я считаю юнит-тесты более важными? Почему, по-вашему, я спрашивал «для всех случаев жизни»?
Для тех, где не достаточно функциональных, можно сделать рефакторинг. Вообще всегда можно сделать рефакторинг, наверное в этом и есть основной посыл почему KISS работает.
Рефакторинг, чтобы не писать unit-тесты? Супер. И чем unit-тесты мешают KISS? Если вместо одного unit-test'а придётся написать десяток функциональных, это по-вашему simple? Впрочем, мне на сегодня хватит открытий, вам тоже удачи )
Наоборот, если хотим что-то протестировать изолированно — рефакторим в отдельный модуль.
В момент, когда появилось упоминание кирпичей, ржал в голос! =)
А мне захотелось заплакать. Потому что, это правда.
Я стараюсь получать от работы фан, а не уныние, даже в таких случаях =)
Я тоже старался в одной конторе, пока не получил на портирование проект, написанный моим однокурсником, получившим-таки вышку (я из универа вылетел, кодить учился сам). Сначала было смешно читать его код, в котором всюду были: копипаста, структуры вместо классов, никаких внятных иерархий, очень большие и тупые функции (рекорд — примерно 3500 строк — ядро игры), очень мало комментариев (те, что были, ситуацию не проясняли), совершенно дурацкие и бессмысленные имена функций и переменных (был даже такой шедевр — nogi2head, до сих пор смеюсь, когда вспоминаю). Но после месяца возни в этой куче кода хотелось плакать и убивать. К сожалению, он уволился, и убивать стало некого (:
Вот для этого и нужны хорошие архитекторы, чтобы такого не получалось.
Тут две крайности, истина где-то посередине. У одного — явный over-engineering, у другого антипаттерн God Object и спагетти-код. Поддерживать сложно и то, и другое.
Хорошие архитекторы не помогут при меняющихся требованиях.
Как раз таки должны, если хорошие. Нужно заранее спланировать архитектуру и стратегию ее развития (в определенных рамках), а также выбрать фреймворки и технологии.
Если формулирование задачи происходит посредством паттерна «испорченный телефон», или даже шире, если заказчик сам не знает, чего хочет, то никакие архитекторы проект не спасут. Вменяемость важна на каждом этапе, как это не печально =)
Иделаные архитекторы в идеальном мире может быть.
Нет я серьезно, никакой гений-архитектор вас не спасет при изменчивых требованиях. Изменчивые требования одна из двух главных причин провала проектов. Почитайте — Роберт Гласс. «Факты и заблуждения профессионального программирования»
То есть, вы считаете, что раз уж требования меняются, на архитектуру можно смело класть болт?

В том то и дело, что архитектура меняется со временем жизни проекта, и ей тоже надо управлять. Говоря про архитектуру, я ни в коем случае не имел что-то, что надо придумать/построить/спроектировать нечто гениальное, закрыть семью печатями, и никогда не менять.
Думаю, имелось в виду, что архитектура должна соответствовать текущим требованиям, в крайнем случае предусматривать расширение в наиболее вероятную (по опыту разработчика) сторону, но не так, чтобы лёгким движением руки CMS превратить в OS, желание чего проглядывает в левом столбце топика.
А с чего вы взяли, что от изменчивых требования нужно спасаться? Гибкость — это то, что как раз и нужно заказчику. Потому и архитектура должна быть гибкая, а цель архитектора эту гибкость обеспечить.
Часто встречаете заказчиков, которые упоминают про гибкость? Если да, то искренне завидую, можно трудозатраты на гибкость декларировать явно, а не прятать их в других пунктах.
Я видимо плохо выразился, я имел в виду, что заказчик подразумевает гибкость требований, а не гибкость кода, как мы уже там эту гибкость переварим — наша проблема. И как это ни парадаксально, видимо самый дешевый способ быдерживать изменения требований, то есть гибкость — это в меру хардкод, прямо как в примере из поста пишет Маркус, и готовность рефакторить и переписывать. Закладывание гибкости в код обычно результатов не дает: никогда не угадаешь где поменяются требования, а делать гибким все подряд приведет лишь к черезмерной сложности.
Это не связанно. Даже когда требования меняются, хороший архитектор лучше плохого.
Можно обобщить эту мысль — хороший лучше плохого )
С этим не поспоришь. Однако на ходу архитектуру менять невозможно, а требования могут меняться.
Например проект разрабатывался в течении 5 лет, огромное количество времени ушло на тестирование (тестирование верстки, функционала, нагрузочное тестирование и т.п.) и отладку, и сейчас все работает стабильно. И тут заказчики хотят новую фичу. Фича никак не ложиться на архитектуру проекта, 5 лет назад не кто не мог предположить что она понадобиться. И еще нам конечно нужна обратная совместимость. И что вы предлагаете, перелопачивать архитетуру ради этой фичи? А потом еще год тратить на отладку тестирование и стабилизацию? Ну да архитектура будет «правильной», но какой ценой! Никакой зказчик на это не пойдет, да и разумный разработчик тоже. Соотвественно старый код не трогается, а новая фича дописывается где-то сбоку, при этом нам нужно будет только отладитть и протестить новый код, т.к. старый почти совсем не застронут. А как это выглядит с точки зрения архитектуры? — естественно это кастыль. И в реальных долгоиграющих проектов таких костылей великое множество. Чем больше таких изменчивых требований тем больше костылей, и в какой-то момент все это чудо может начать сыпаться.
Автор, как мне кажется, перевирает.
>> придётся чуть поменять логику метода createBread
А там кровь, кишки, расчлененка. И уже не чуть менять надо…
UFO just landed and posted this here
Я тут вижу два антипаттерна :)
Вообще, менеджер виноват в этой ситуации.
На каждое такое серьёзное усложнение задачи он должен отвечать, что это будет целиком новый проект, который и оплачиваться должен соответственно. И начинать его надо с нуля, и тогда таких вавилонских башен городить не нужно.
Ага, ага — не сдав предыдущий он должен заказчика развести еще на пяток по отдельному прайсу ;-)

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

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

> «Хм», — произносит Борис и вспоминает про шаблон «строитель»
Да вы издеваетесь! Сколько можно?
Сказочка смешная, конечно, только вот в конце все на самом деле немного по-другому бывает.К тому моменту, когда менеджер приходит в шестой раз, класс у Маркуса достиг длины в три тысячи строк, из которых две с половиной приходятся на метод createBread, внутри которого находится замысловатая кострукция из семикратно вложенных if-ов. В этот момент Маркус обнаруживает, что, несмотря на все старания, он не может выполнить требование менеджера, поскольку от любого изменения его класс по мистическим причинам перестает работать. Так что далее Борис уже общается с менеджером в одиночку.
Борис еще занят пред-предыдущей хотелкой, ему не до общения ;-)

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

Весь день откладывал статью от чтения и не пожалел, что наконец-то уделил время. Какой это типический сюжет.

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

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

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

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

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

Не совсем верно. Он моделировал свои представления о процессах и объектах. Задача была «чтоб делался хлеб». Моделировал бы он реальные процессы и объекты у него не появился бы на первой же итерации абстрактный класс Product и ещё более абстрактная фабрика ProductFactory.

Второй… моделировал только объективные факты предметной области, не торопясь забегать вперед.

Тоже не совсем верно. Скажем, когда он ввёл параметр bredType:int, то тоже отошёл от объективных факторов. Объективные факты были хлеб, пирожки с мясом, пирожки и капустой и торты, а не номер типа.
Он моделировал свои представления о процессах и объектах.
Лучше так: Он пытался по настоящему моделировать процессы и реальные объекты описывающую задачу, но в результате постоянно получалось моделировать свои представления о процессах и объектах под соусом постоянно меняющихся искаженных представлений о задаче менеджера )))

на первой же итерации абстрактный класс Product и ещё более абстрактная фабрика ProductFactory
Там вроде фабричный метод у него был.

Скажем, когда он ввёл параметр bredType:int, то тоже отошёл от объективных факторов Ну он же программист: взял самое простое решение формализации вместо каталога паттернов ))) Это кстати правильно на первых этапах. Художник, скульптор начинает работу «грубыми мазками» еще нет четких очертаний. Второй тоже так-же сделал: «тупо воткнул параметр» не мудрствуя…

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

Согласен, но речь идет конечно об простейшей формализации сущностей.

Простейшая она с точки зрения реализации, и то не факт. Была у него необходимость сводить вид сущностей к целому числу, абсолютно нейтральному семантически? Пускай он даже в теле метода использует константы или перечисления, но сигнатура метода нам ни о чем почти не говорит. Сделал бы перечислимый тип — было бы лучше.
Была у него необходимость сводить вид сущностей
да по-моему ему вообще все пофиг. Выбрал самый простой способ, понимая, что потом все равно исправлять или постановка 100 раз поменяется…
UFO just landed and posted this here
в сторону Си и смотреть не хочу
Ваш поход — это ещё одна крайность. Я вас ни в коем случае не осуждаю, если вы делаете это просто для души. Но я бы не советовал применять такой подход для более-менее серьёзных разработок для МК: кода получается больше, его труднее читать, дольше писать (приходится вручную писать тривиальные для C вещи), а о портируемости даже железно-независимой части кода сразу можно забыть. Ну и всё зависит от решаемых задач: если нужно не просто поиграться с МК, а сделать полезное устройство, лучше воспользоваться библиотеками на C и решить задачу быстро и эффективно, чем писать всё с нуля на асме и решить задачу только эффективно (:
UFO just landed and posted this here
К слову, полепить классы и шаблоны на МК вам не всегда дадут: не для всех семейств МК есть компиляторы C++. Понимая немного в разработке компиляторов, я не осуждаю разработчиков софта для МК, т.к. C++ очень сложен и не имеет стандартизированного ABI.
А зачем для классов C++? Их можно и на С, и даже на ассемблере писать, если это очень нужно. Вот только эффективно закодировать на ассемблере хотя бы вызов виртуальной функции уже не просто.
Вот затем и нужен, что в С и асме классов нет. Я понимаю о чём вы говорите (в <a href=",">этой статье я описываю такой подход для написания драйверов GPIO), но это всё же костыли. Я был бы совсем не прочь заиметь язык попроще, чем C++, но с нормальными классами (иногда ООП выручает).
этой статье
Отправил случайно (:
Впрочем, я сейчас just for fun пишу компилятор языка — этакой замены C: низкоуровневый, как С, с возможностью линковки и вызова сишного кода, но также с возможностями метапрограммирования и фиксированными размерами целых типов на всех платформах (для начала сойдёт). Сдаётся мне, что я его доведу до юзабельного состояния раньше, чем мой любимый D станет доступен на Cortex-M3.
Будете делать препроцессор — сделайте возможность передачи макросу в качестве параметра куска/блока кода, а не только имени переменной или выражения.
UFO just landed and posted this here
Раз уж вы их так любите (я тоже), вы должны оценить библиотеку protothreads.
UFO just landed and posted this here
Мне кажется, что в ПЛИСах триггеры-сумматоры поближе будут, чем в МК. Да и более изящно они выглядят. Попробуйте — вам понравится, когда одновременно выполняется большое количество операций.
Он [Борис] создаёт класс Recipe, а к нему — строитель RecipeBuilder. Рецепт он внедряет (ВНЕЗАПНО!) в печку с помощью сеттера setRecipe(recipe:Recipe).

А Маркус (вы не поверите) добавляет ещё один целочисленный параметр в createBread — recipe.


Это пять! Рецепт ну никак не может быть меньше объекта с шагами и ингредиентами. Это же рецепт! А у Маркуса это просто int? Integer? Целое число? Это же что за бомба там в реализации? Куда Маркус захардкодил все остальные данные? Неужели в createBread? Всё это автор скрыл, оставив только одобрительное «вы не поверите».

А что сделал Борис? Он сделал объект. Правильно сделал. Но чтобы показать неправильность кучи классов, автор додумал ошибку с ненужным билдером, и setRecipe, хотя вполне можно было бы передавать рецепт в cook(), как это сделал Маркус, и вообще было бы зашибись. Какой плохой Борис!

Итого: реализацию рецептов Бориса надо просто чуть-чуть улучшить, а жесть от Маркуса надо вычищать веником вместе с Маркусом! Но из статьи выходит, что Маркус молодец. Не зачёт.
Разумеется, рецепт — целое число. Это индекс в массиве, в котором записаны разные рецепеты. И вовсе это не 1,2,3,4, а 0,12,42,97… — ведь разные рецепты разной длины! Элементы массива — команды виртуальной машины, интерпретатор которой занимает центральную часть метода (switch на полторы тысячи строк, зацикленный через goto) и их параметры. Параметры прописываются перед запуском цикла прямо в массив, исходя из дополнительных данных (например, режим подачи газа или максимальная мощность микроволновки). Возможно, в особо сложных случаях, модифицируются и команды.
Да, команды, разумеется, это просто целые числа. Без символических имен. Зачем? Автор и так их прекрасно помнит, а если забыл — достаточно посмотреть в соответствующий case.
Впрочем, нет — главный цикл занимает одну строчку:
  while(cmd=A[p++]) (*F[cmd])(&p);
UFO just landed and posted this here
Веселая сказка :)
Вспомнился один из топиков до этого с таким же примером.

А вот что удивило — почему никто из отписавшихся не вспомнил что надо сохранять исходники? ;)
Вообще кто-то задумывается о сохранении исходников? Ведь в результатах на много сложнее всем потом разбираться.

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

То же самое о требованиях менеджера — прописать везде «этот класс является прямым следствием исходного требования норме 123 » текст требования ...".

А потом вдруг выясняем что хлеб нам печь не надо и мы раз… выбираем все что возникло на основании этого требования и не имеет более исходных зависимостей и разом удаляем ;)

Или у нас есть исходное требование — «поддержка разных платформ». Мы его вносим во что-то (не голову, не документаци и не в ТЗ) в программе/среде разработки. И добавление какой угодно плюшки одной платформы тут же вызывает ошибку не соответствия требованиям. Даже если менеджер ее просит добавить — его требование тут же вызывает ошибку… которую… надо решать меняя или удаляя ранее установленные требования.

А вы все эти исходные требования выкидываете как мусор, а потом за голову хватаетесь — как же нам поддерживать эти «бинарники» из кода C++ без «исходников» — требований :)))
Маркус безнадёжен. В 99% случаях неиспользование нормального ООП и паттернов там, где они напрашиваются — следствие узкого кругозора разработчика, а не мудрости. Маркус, похоже, не знаком с понятием технического долга и ненавидит всех, кто будет сопровождать его код.

То, что у Бориса много квадратиков, а у Маркуса — мало, ничего не означает. Помимо отвлеченной схемы классов автору следовало привести то, что внутри. У Маркуса там 100% адский ад.

Почему-то в последнее время стало модным отрицать заповеди Макконнелла и Фаулера. Это очень удобно, не надо развиваться, изучать теорию, исписывать 100500 страниц в блокноте в попытке найти красивое решение. Зачем? Можно дать себе индульгенцию на говнокод, оправдываясь меняющимися требованиями или погодой или тем, что «надо было срочно». Фигня всё это. Разработчик либо рефлексирует, либо пишет код в стиле дамп потока сознания, как Маркус.
Однажды группе программистов выдали одно задание — каждому. Эдакое соревнование. Победил самый тормозной и самый неподдерживаемый код а-ля Marcus-style.

Почему? А к указанной дате он единственный который работал правильно.

;-)

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

Спасибо за интересную статью. Есть основания полагать, что код на Haskell получается и простым для понимания и легким в сопровождении одновременно: eax.me/haskell-bread-task/ В принципе функциональный подход можно применять практически в любом языке.
В принципе функциональный подход можно применять практически в любом языке.
Да, и обычно это называется «шаблоны проектирования».
ой ли? какие есть функциональные шаблоны проектирования?
Они не нужны. Как пишут Петер Норвиг (1996) и Пол Грэхэм (2002), шаблоны проектирования элиминируются в функциональных языках и представляют из себя промежуточный продкут ручной компиляции с функциональных языков. Т.е. ручной перевод с функциональных языков на С++. Так что если вы будете применять функциональный подход в языке более низкого уровня, у вас — внезапно — будут — сами собой — вырисовываться «шаблоны проектирования». Даже если вы о них до этого ничего не читали (поэтому, я думаю, при них читать в общем не надо).
Так что если вы будете применять функциональный подход в языке более низкого уровня, у вас — внезапно — будут — сами собой — вырисовываться «шаблоны проектирования».

можно пример?
Прежде всего вылезет «стратегия», более того, вам будут говорить, что «функциональное программирование — это всего лишь синтаксический сахар для шаблона Статегия». Ещё у вас будут регулярно «угадывать» «цепочку ответственности», «состояние», «медиатор», «command object» (это в случае карринга) (и даже в чём-то будут правы).
Недавно, кстати, ходил на собеседование. Интервьюер прифигел, когда я ему сказал, что интерфейс в Java есть всроенная в язык реализация шаблона «стратегия».
Не знаю как для java, но если бы вы это сказали про .Net или С++ то я бы ответил, что вы только что назвали один из способов реализации шаблона «стратегия» самим шаблоном. Это примерно как назвать прогрммированием набор текстк исключительно в IDE, хотя IDE это только инструмент.
  • С++, как «объектно-ориентированный язык» — это вообще «первый блин комом». Интерфейсов в нём нет, есть только множестенное наследование от классов.
  • Если есть реализация, встроенная в язык, то а) зачем делать другие реализации б) зачем называть её термином не из языка, а из книжки про паттерны. И я интерфейс стратегией не называл, я и назвал его — «встроенной реализацией шаблона».
  • Про IDE — так на Java набор кода без неё — это баловство, а не серьёзное программирование. Качественного кода без рефакторинга и форматирования получить нельзя, а средствами IDE это делается в сотни раз быстрее, и занимает много меньше времени, чем набор кода. С другой стороны, нужен ли качественный код на таком «write-only» языке как С++ — тоже вопрос. Иногда нужен, даже ценой замедления разработки раз в 10.
А почему именно стратегии? Я думал, что интерфейс — это реализация паттерна интерфейс (полнсотью асбтрактного базового класса)
«Ну может быть, стоит слегка поменяет реализацию createBread().»
Ага, совсем чуть-чуть. :)
Статья поучительная, одобрям-с… Узнаю в Борисе себя на заре освоения ООП. Сейчас я всё же скорее Маркус, чем Борис…

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

Еще замечание на счет Маркуса. Почему-то все комментирующие решили, что весь код Маркуса должен быть собран в методе сreateBread().

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

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

Поэтому считаю, что Маркус таки да… Может считаться примером для подражания… ))
Видел я такие суперклассы и суперметоды, разница небольшая, и туда и туда лезть страшно, трогать опасно.

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

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

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

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

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

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

Обратите внимание: Борис принял о выделении кирпича в отдельный класс не исходя из требований задачи/менеджера, а исходя своих знаний о реальном мире. В реале кирпич сильно отличается от хлеба. Хлеб едят, а кирпич нет… Но какое это имеет значение к контексту задачи, не понятно. В контексте задачи в конечном счете может оказаться важным, что хлеб и кирпич оба являются брусками, имеющими параметры длину*ширину*высоту. В этом случае разделение сущностей не обосновано.

Поэтому я считаю, что Маркус совершенно правильно сделал, что не стал городить лишних сущностей. Задача на том этапе этого не требовала.
Тут выше уже говорили, добавить лишний класс и поменять тип параметра у createBread Маркуса — не проблема. А вот убрать избыточные сущности уже задействованные в структуре приложения, это реально будет геморрой. Борису придется рефакторить до посинения.

Главный критерий в данном случае — это эффективность программиста, сколько времени он потратил на реализацию затребованного функционала. Прикиньте сколько времени потратили Борис и Маркус на реализацию своих вариантов решений. А теперь представьте себе, что Борис и Маркус работают на почасовой оплате…

Обе реализации архитектуры приложения выглядят криво, но постановка задачи, извините, еще хуже… Но иначе получится не могло. Каков вопрос, таков ответ…
У Бориса я вижу три основных ошибки:
— преждевременное абстрагирование (на самой первой итерации создал класс Product. от которого унаследовал Bred и так постоянно)
— необоснованное применение знаний реального мира (ввёл повара, кирпич отличил от хлеба, а пирожки с тортом сделал счёл однотипными с хлебом сущностями, хотя возможно как раз кирипчи с хлебом в данном контексте однотипные (монолитны), пирожки другой тип (требуют начинки перед выпеканием), а торт совсем другая — требует «наичинки» после выпекания)
— злоупотребление паттернами до момента когда их необходимо использовать
Клёва! Элегантно и ничего лишнего. Вы разожгли во мне огонь интересующегося лиспом. Спасибо за труды. Lisp вдохновляет.
Если все-таки предположить, что у двух главных героев поста, вымышленных программистов Маркуса и Бориса, есть реальные прототипы, кем бы они являлись? На роль Маркуса, пожалуй, подходит Маркус «Notch» Перссон (совпадение имен, несомненно, случайно), а вот кем мог бы быть Борис Н.?
как же я ждал этого коммента)
Ииии? :) Интересно же!
Бориса ищите среди отечественных разработчиков
Ну, это значительно сужает круг поисков!
Спустя полтора года поисков, я его все-таки нашел! Его зовут Борис ████████вич Н███████, это широко известный в узких кругах специалист в области программирования и всего такого.
Как продвигаются поиски с вашей стороны?

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

При чём тут вообще программисты? Если менеджер проекта настолько плох, что не в состоянии узнать элементарно — для чего нужна печь, тут как ни пиши — лотерея. Маркус молодец — попал пальцем в небо. Только в жизни такое бывает редко. И его простое на диаграмме решение в реализации и отладке может занять больше времени, чем «навороченное» решение Бориса. Которое всегда можно подвергнуть рефакторингу, в отличие от решения Маркуса, которое проще выбросить и написать заново. Обычно это делает уже другой «Маркус».
*Причём, конечно…
«При чём» выглядит более правильным. «Причём» — это приблизительный эквивалент «кроме того», а «при чём» — «что оно тут делает». Хотя, я могу и ошибаться.
Не согласен. Имхо, как раз решение Маркуса проще подвергнуть рефакторингу. Проще вычленять сущности, инкапсулируя куски «спагетти», чем «инлайнить» необоснованно введенные абстракции.
Проблема спагетти-кода — в его запутанности, а не объёме. Нет никакой проблемы рефакторить большое количество слабо связанных классов, а распутывать одну большую кучу взаимосвязанного кода — то ещё удовольствие. Впрочем, если вам нравится — на вкус и цвет товарищей нет )
При одном уровне запутанности я предпочту рефакторить простыню кода, чем ту же простыню разбитую на необоснованные абстракции.
Скорее всего,. на менеджере висит десять проектов, и ему просто некогда «узнавать элементарно».
В статье не хватает картинок, какой хлеб у обоих на выходе получается. Как я понимаю, у Бориса класс бред (class Bread), но что же у Маркуса? (int)0x80030050?
Не вкусно! )
Обычный объект Хлеб. Такой же вкусный как и тот что слева. То что вы не знаете, что он «Продукт» не делает его менее вкусным.
Ну, ожидать «обычный вкусный объект» от девелопера, который только что накатал 2000 строк write-only кода в единственном методе, это слишком оптимистично.
Если, конечно, там на одного Маркуса целый отдел QA, то они конечно добьются требуемых потребительских качеств. Но в реальности, любой адекватный руководитель заставит такого «разработчика» самого писать тесты на этот код. И тогда диаграмма компонентов (вместе с немаленьким самопальным фреймворком тестирования) будет куда как печальнее
Кому доводилось рефакторить методы из тысяч строк — меня понимает)
Ну почему же в единственном?
То, что у класса один публичный метод, не значит, что у него только один метод.
На самом деле справа в первом примере класс Менеджер лишний вплоть до введения печки. Делался хлеб = создание объекта Хлеб. Во втором примере и далее Менеджер = Печка, которая печёт хлеб, вот тут и надо вводить класс Печка/Менеджер. Так в целом справа отличный подход. Пользователю вашей библиотеки нужно от интерфейса не больше и не меньше, чем требует логика приложения. Ему нужно сначала Хлеб, потом Печка, а в конце Кирпич. Вот только сразу возникнет вопрос у пользователя библиотеки Маркуса: «А где печка, что я заказывал?». Не думаю, что ответ: «Менеджер — это на самом деле Печка!», — его устроит. Скорее всего следующий вопрос «А нафига?!» своей риторичностью приведёт Маркуса к небольшому рефакторингу по переименованию Менеджера в Печку.
Вдруг потом окажется нужным выпекать не только в печке, но и на паяльной лампе и на прутике в костре?
Тогда и потребуется переименование Печки в Менеджера, раньше не надо.
Маркус вроде не думает о «вдруг потом окажется». Когда окажется, тогда и реализовать должен «менеджера» который будет решать что и где печь/обжигать.
Три ступени овладения дао копания:
  1. Не могу копать
  2. Могу копать
  3. Могу не копать
  4. Могу допату так засунуть, что фиг кто найдёт

При этом третья стадия напоминает первую…
Ох уж этот Фрейд… открыл интересную статью про выпечку, а увидел на главной картинке вовсе не хлеб! Даже сначала по быстрому свернул все окна, пока осознавал «что это было, пух?»
Возможно проблема у Бориса была в самом начале. Абстракция должна быть не в данных, а в сервисах. Если готовим хлеб, значит должны получить хлеб, если кирпич — значит кирпич. В этом плане Маркус правильно делал.

public class Bread
{
   public bool IsWhite {get;set;}
}
public class Brick
{
   public bool IsRed {get;set;}
}
public interface IRecipe<T>
{
   T Cook();
}
public class BreadRecipe : IRecipe<T>
{
   public BreadRecipe(bool isWhite, IBackingOven oven)
   {
   }
   public Bread Cook()
   {
   }
}
public BrickRecipe : IRecipe<Brick>
{
   public BrickRecipe(bool isRed, IBuildingOven oven)
   {
   }
   public Brick Cook()
   {
   }
}
public interface IRecipeFactory
{
   IRecipe<T> Create<T>(string name);
}

В остальном, Борису не мешало бы убить в себе перфекциониста, а Маркусу — всё же почитать про паттерны и тестирование кода.
Жалко, что это не перевод. Так хочется поделится статьей с коллегами.
Пусть Русский язык изучают, а то так и останутся несведущими.
Это. Очень. Хорошо. Как можно было это пропустить, нужно в учебники добавить

Интересно, автор сознательно применил набор манипуляций или это получилось само собой?

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

А в реальности, если кто-то использует инты вместо перечислений или объектов, то это конечно, сразу красный маркер.

хлипкий Хаселист попался

за синтаксис не поручусь, но и так все ясно

class Recipe
  ingredients: List[Ingredient]
  description: str

class Ingredient
  name: str
  weight: float

class type Cookie
class Bread : Cookie
class MeatPie : Cookie
class PotatoPie : Cookie
class type IOven
class GasOven : IOven
class ElectricOven : IOven
state On | Off
class Gas
ovenOn IOven -> Gas -> state -> (IOven state)
cook : IOven state recipe-> Maybe Bread

а его уже можно перевести в шарп через ЧатГПТ код ибо никто на Хаскеле нынче не пишет

using System.Collections.Generic;

class Recipe
{
    public List<Ingredient> ingredients;
    public string description;
}

class Ingredient
{
    public string name;
    public float weight;
}

class Cookie { }

class Bread : Cookie { }

class MeatPie : Cookie { }

class PotatoPie : Cookie { }

interface IOven { }

class GasOven : IOven { }

class ElectricOven : IOven { }

enum State
{
    On,
    Off
}

class Gas { }

record OvenOnRequest (IOven oven, State state)

record CookRequest(IOven oven, State state, Recipe recipe)

class SomeShit
{
    IOven ovenOn(IOven oven, State state, Gas gas)
    {
       ...
    }

    Maybe<Bread> cook(IOven oven, State state, Recipe recipe)
    {
        return new Maybe<Bread>();
    }
}

итого минут 10 на бумажке, не отлаживал

... ну или state засунуть внутрь IOven но лень

Блин, на перевод на шарп времени больше ушло исправлять за чатГПТ, да и то не уверен

Articles