Комментарии 328
А менеджер вообще знает, что он хлеб печет? Судя по его заказам — не знает.
Часто во многих местах пишется говнокод. А потом, переправленный на сто раз и весь в заплатках говнокод, переписывается, когда заказчик доволен всем и это оказывается именно то что он хотел.
К слову, за 20 лет работы я ни разу не видел 100% выполнения первоначального ТЗ. Просто потому, что этот гроссбух устаревает еще до того, как на нем чернила просохнут.
И всегда эта игра в бумажки заканчивается «мы можем из этого провала вытянуть нечто сферическое в вакууме, но нам надо еще 1 год и пять лямов» (Ц), и еще очень хорошо, если количество итераций ограничивается тремя — пятью, а то ведь цифры бывают и двузначными.
Когда заглянем в солюшен эксплорер засомневаемся, а когда заглянем в исходный код — картинки поменяются местами.
Много лет назад был тренд переусложнения функционала, до появления гибких подходов к разработке.
Сегодня все чаще начинаешь встречать злоупотребление принципом KISS.
Когда такой продукт достаточно развитый его уже невозможно переписать, а стоимость внесения изменений начинает стремится к бесконечности.
Хотите быстого старта — используйте готовые фреймворки.
Архитектурное планирование необходимо. Надо придерживаться разумного балланса.
Хотите быстого старта — используйте готовые фреймворки.ХЗ. Если эта фрамуга — не Гуава, то есть почти 146% шанс, что придётся допиливать или дообвязывать.
Проблема Бориса — бездумное воспроизведение всего, что он увидел в умной книжке. То есть он не мыслит самостоятельно, гроше цена такому разработчику до того момента, пока он не начнёт задумываться над тем что делает.
Проблема Маркуса — отсталость и не имение представления о том, какие вообще паттерны и средства существуют на данный момент. Он не видит перспективы и общей архитектуры проекта, а реализуют чётко поставленную задачу. Короче этот товарищ скорее малоопытен, недостаточно любознателен или просто ленив.
В любом случае, отчасти это вина проект-менеджера. Именно в его обязанности входит грамотная поставновка полной задачи, взаимодействие с заказчиком и проработка архитектуры будущего приложения совместно с разработчиком. Так что подтверждаю Ваши слова, «Архитектурное планирование необходимо». А иначе — это просто игра в песочнице, а не серьёзная работа.
Приведу пример из своей жизни. Мы перемещали бизнес-логику несколько раз, в разные места. Реализовывали ее несколькими способами. Рефакторили только потому, что нужно было обеспечить ряд требований, которые возникли в процессе использования систем. Некоторые архитектурные решения умерли в процессе прототипирования, потому что мы напоролись на деградацию сервиса. Это предусмотреть было просто нереально. Некоторые решения были отброшены как неоправданное усложнение. От кода, который был пять лет назад, остались только жалкие наметки, это касается общей организации файлов и методики обработки запросов.
И если меня спросят, как бы я делал аналогичный проект сейчас, я бы снова внес ряд улучшений к тому, что работало годами. :) Стал бы я много времени тратить на продумывание архитектуры? Скорее всего нет. Не потому что я хочу напороться на проблемы в будущем, а потому что я знаю, что я напорюсь на них, но не знаю, на какие именно. У меня есть ряд обалденных заготовок, которые отточены временем. Они достаточно универсальны для проектов любой сложности.
На практике всегда так и есть. В исходном коде в случае Маркуса будет ад, который все равно придется рефакторить, в случае развития проекта и приводить к версии Бориса, на что уйдет еще больше времени, чем писать сразу расширяемую версию.
Я с такими неосознанными KISS уже достаточно намучался. При чем переписать проект полностью в таком случае проще, чем править частями. И вот тут ситуация, что бывает у бизнеса нет времени или ресурса на это и тебе предлагают либо поддерживать что есть, либо мы тебе замену найдем.
Лично мне картинка Бориса нравится больше и в ней нет ничего сложного на самом деле. Я уверен и код внутри всей структуры не такой уж сложный.
Версия Маркуса может быть актуальна только в случае быстрого стартапа, но это будет протототип и его придется выкинуть.
Хотя второй подход, бесспорно, лучше, если изначально сущностей немного. Тут же Печь, Пирожок и Кирпич, остальное — объекты-значения. А расплодили в первом подходе выше крыши
В итоге я перешёл к буквальной реализации требований минимальными силами. Писал код 2-3 дня, демонстрация, поправки, ещё 2-3 код, ещё демонстрация и т.п. Таким образом мы очень быстро пришли к согласию.
Тут важное, на мой взгляд, слово «начинает». А вот продолжает опытный программист уже исходя из своего багажа знаний да и шестое чувство достаточно развито. «Перваш» же может наделать кучу ошибок по неопытности.
В итоге я перешёл к буквальной реализации требований минимальными силами. Писал код 2-3 дня, демонстрация, поправки, ещё 2-3 код, ещё демонстрация и т.п. Таким образом мы очень быстро пришли к согласию.
[sarcasm]Это тот самый agile, который дает результат без скрамов, митингов, досок и прочих странных вещей с непонятными названиями. К сожалению, если использовать его именно так, то менеджеры окажутся не нужны и их уволят, поэтому большинство программистов все еще остается командой с гибкими и прогрессивными методиками разработки и нерабочим продуктом.[/sarcasm]
Когда в итоге самый опытный начинает писать код также как студент первого курса.
Потому, что знает, что отрефакторить
Спасибо за статью :) Качественно написано
Да, и я в этой истории на стороне Бориса, как ни странно, в его ахритектуре больше информации. Да, немного с избытком. Чтобы навести порядок в его коде — достаточно будет 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
// Комментарии тоже никто не отменял
manager.createBread(17, 42)
. Или, и того хуже — передать в аргумент «тип печки» значение из «вида рецепта».И это не последняя причина для чуть большего размышления об архитектуре.
Ибо у настоящего спагетти-код-дел-мастера Маркуса makeBrick мог бы и не оказаться отдельным методом, а всего лишь отдельным типом хлеба. И в его коде createBread можно вызвать с кодами, соответствующими «в мартеновской печи изготовить бородинский кирпич».
Всё, что нас не убивает, делает нас сильнее, угу.
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;
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++.
Окей, хотелось как лучше. Но всем известно куда вымощена дорога с такими благими намерениями. Возможно виновата подача, но у меня на протяжении всей ветки обсуждения перед глазами стоял очередной воин, вызывая раздражающие, болевые позывы в одном месте.
namespace BreadType {
enum {
White = 1,
Black // etc
};
}
Так, может, это вносит свою лепту в 90%?
Ключевое слово «зная». А в ситуации когда программист первой итерации уволился, а у менеджера остались максимум записи того, что он говорил на первой стадии, а то и этого нет, а есть продукт у которого есть исходники и который вроде текущим требованиям удовлетворяет, я бы предпочёл чтобы мне в наследство достался код Бориса, а не Маркуса.
Использовать цифровые типа без описаний конечно не дальновидно
Имхо, если и выносить подобные вещи в базу, то использовать не суррогатные числовые ид, а текстовые. что бы вместо кода типа i
f (bread.type == 1)
был код типа if (bread.type == 'black')
, а суррогатные использовать только для связи между таблицами.Весь код надо будет переписывать, как у Бориса (хотя тут видно что Борис — тоже не супер спец, так много сущностей плодить не обязательно было сразу).
Вот представьте, есть у вас «плоский» скрипт на PHP на тысячу-другую строк, который вызывается веб-сервером, или программа на Си из одной функции main(), или программа на Java из одного класса с одним методом main. Для покрытия его тестами без рефакторинга придётся, как минимум, эмулировать веб-сервер, а с помощью IDE мы можем безопасно (ну, считая, что разработчики IDE не допустили ошибок) разорвать зависимости логики от получения параметров программы из окружения, чтобы иметь возможность эмулировать окружение в среде тестирования и тестировать логику, а не создавать виртуальное окружение и тестировать всю программу целиком.
Если всё равно не понятно о чём я, могу привести чуть позже примеры на PHP.
Плюс еще в одном месте пришлось переписать MergeSort на QuickSort — вроде рефакторинг, с сохранением внешного контракта, а IDE ничем помочь не может. Тут тоже тест помог отловить баги.
Поясню. Если вы делаете что-то мелкое, то вряд ли вы будете использовать методы Бориса. Оно мелкое, его результаты легко проверить на практике, а отладку делать при помощи нескольких брякпоинтов.
Как вариант — написание чего-то сильно критичного к скорости работы. Надеюсь, вы не будете отрицать, что при прочих равных вариант Маркуса будет работать быстрее варианта Бориса?
Ну а нагромождение классов и тесты стоит использовать там, где нужна надежность и стабильность работы.
Я к чему все это… Не стоит забивать микроскопом гвозди, а на бактерии смотреть сквозь лупу.
На моей практике такого ни разу не было. Всегда ПМ приходил со все новыми дополнениями а ля «а еще печка должна сталь выплавлять, причем уже вчера».
По неопытности пытался делать по-Маркусовски, потом этот проект затянулся на 2 месяца багфиксов, которые вылезали в непонятных местах после каждого такого дополнения.
А вот покрытие тестами мне жизнь хуже еще ни разу не сделало.
Есть у нас продукт, а точнее два файла один на 40к строк кода, второй на 60к строк и это только .aspx.cs, сама вёрстка в два раза больше. Дак вот, это и есть тот случай. Понять, что там происходит — просто не реально.
Чтобы проверить код Маркуса для начала необходим будет сам Маркус, причем достаточно свежий. Который сможет по номерам сказать какой тип булочки под номером 4, и какой тип печи под номером
Я не думаю, что у Маркуса все так запущено, даже студент догадается сделать тут enum.
Вообще в большинстве случаев при разработке любого проекта, аналогов которого нет, то есть реально новый функционал, которые необходим заказчику, а тот в свою очередь если не программист никак не может продумать всей архитектуры, а особенно объяснить все детали проект-менеджеру. В таких случаях всегда разрабатывается прототип и подгоняется его работа под требования без всяких свистелок и прочей мешуры, должно просто выполнять основные задачи для работы данного проекта. И только потом на основе этого прототипа создаётся архитектура и всё это обвешивается классами и рефакторится 10 раз. Все выводы из собственного опыта автоматизации разного рода бизнеса, когда понять задачу было просто очень сложно понять изначально всех мелочей каждого бизнес-процесса, всё это только в процессе предварительного тестирования и запуска прототипа возможно выявить.
Основная проблема Бориса в том, что он сразу стал генерализировать и обобщать сущности, порождая при этом новые сверх необходимости. В этом его основная ошибка. Кроме этого, сущности должны отличатся поведением.
Ну а у Марка конечно, будет спагетти внутри, что конечно же скажется на сопровождении.
В общем, это две крайности, которых следует избегать.
>> А переписать всё заново, если что, — это всегда успеется.
Вот с переписать всегда проблемы. Если проект на 1к строк, то конечно, можно и переписать, а когда там уже 1кк… такое уже не переписывается, а добавлется очередной адъ внутрь реализаций. А еще есть такая штука, как обратная совместимость, которая не позволит просто так взять и добавить еще один параметр в метод функции. Вот что тогда будет делать Маркус?
Программисты вообще всегда молодцы, живут в своем мирке и как знают мирок свой так и пишут.
А вот менеджеры, блин, ну неужели они не могу объяснить какой будет проект:
1) прототип для показа — который выкинут и надо код писать быстрее пусть с утечками памяти немасштабируемый и т.д. (Маркус тут даже перестарался, все можно было написать в main()),
2) серезный проект с ТЗ, которого действительно утверждено — надо показать это ТЗ, диаграмки состояний нарисовать, объяснить как в дальнейшем может развиться система. Какие перспективы сопровождения (Борис не виноват что ему не предоставили полной информации, однако он правильно начал с минимумом абстракций, а потом их наращивал, основной бедой его был повар, который является инициатором выпечки — он фабрика, а не плита, плита — инструмент, который на ровне с рецептом должен использоваться поваром и просто поддерживать температуру определенную, из-за неправильного повора рецепт вперся в плиту зачем-то и пошло поехало, Борис действительно не мог остановиться и развернуть архитектуру вовремя из-за косноязычности менеджера, кирпичи выпекать стало не кому выходит, кстати хороший паттерн чтобы не плодить под пирожки своих поворов — мост / Bridge)
3) стартап, в котором надо делать быстро и с возможность масштабирования. Тут любой подход хорош, единтсвенное надо реализовывать его качественно. Если разобратся в подходах данных программистов, то Борис пишит объектно-ориентированный код, а Маркус — процедурно-ориентированный, ну и кто сказал что последний сложно тестировать? Можно и нужно поколоть его основные методы на составляющие и их так же легко тестировать как объекты. Разница их подходов лишь в том, что у Бориса есть модель объектов, она более наглядна, чем математическая модель Маркуса. А код один и тот же, просто дайте процедурнику объектно ориентированный код, он его поймет, но напишит все посвоему и криво. А если наоборот, то объектник начнет все рефакторить излишни пытаясь структурировать. Так что лучше не мешать программистам, а грамотно менеджерам ставить задачки))
Вывод из статьи: менеджеры виноваты во всем
Бывало разное — раз заказчик сказал, что будет прототип, а потом перепишем. Написали, а его жаба заела переписывать — итак работает, мы конечно договаривались, но запилите пока на том что есть ещё пару мелких фишечек. Потом ещё фишечек. Так прошло 2 года, на проекте 2 раза сменилась команда, ну в общем вы поняли…
А что нам, команде, было делать? Тут всё зависит от Product Owner-а. Но это я про мелкий и средний конвеерный аутсорсинг, конечно в продукте всё по-другому.
У Маркуса диаграмма получилась простая, однако весь ад будет внутри методов. В этом варинте юнит тесты невозможны впринципе, возможны лишь приемочные тесты. Чтобы сделать сравнимое покрытие, Маркусу нужно написать в 100500 раз больше тестов, половина из которых будет ломаться при мельчайшем изменении кода.
KISS здравый принцип, но в данном примере им никто не воспользовался.
принцип KISS — «сохрани это простым», а не «сохрани это тупым»,
т.е. не стоит плодить сущностей более чем необходимо, но и менее чем необходимо тоже не стоит.
Ага, и в итоге все это сведется к схеме похожей на первую :) Плюс вы бы подрисовывали квадратики «переписанного почти с нуля кода». А то складывается впечатление, что во втором подходе раз — и сразу все написано.
А схемы Бориса надо было рисовать в «проекции 5D на 2D» чтобы вообще все запутано выглядело (намек на то, что несколько выровняв их можно даже якобы кашу классов сделать понятнее).
Хотя одно верное наблюдение есть:
> Борис уже начинает что-то такое чувствовать, но остановиться уже не может.
Надо знать где остановиться с абстракцией. :) А то можно сочинять и дальше — ведь повар тоже не из вакуума взялся… мама, папа, учителя… :)
Нам нужен Proof of Concept и менеджер идёт к Маркусу. Быстро, эффективно. Концеция прошла демо презентацию и то же задание получит Борис. Вот теперь у компании есть продукт.
Дело в том, что многое зависит от компании и специализации. Компания со своим продуктом уделяет большее внимание проблемам поддержки/правки кода и масштабируемости. Пример из реальной жизни — через год приходит менеджер и говорит: «Помнишь хлебо-печку? Одна большая компания хочет построить хлебзавод и мы уже продали им наш модуль!». А потом менеджер будет приходить ещё много раз с новыми требованиями для API к уже завершённому проекту… И тут вот вам захочется сломать что-нибудь очередному Маркусу. Причём что то жизнено-важное.
Вопрос в том, что такое «проще». Вместо классов использовать дополнительные поля и ветвить логику? Или, наоборот, использовать полиморфизм? Однозначного ответа тут нет.
Зато есть хороший принцип третьего дублирования: если что-то хочешь продублировать в третий раз — пора с условий перейти на полиморфизм.
А заранее создавать абстрактные фабрики, отвязывание по интерфейсам и инъекции — это, естественно, усложнение, а не упрощение. А главная причина создания ООП как раз была в упрощении.
Так что, люди, используем исконные принципы ООП, а паттерны, архитектуры и т.д. — только если они упрощают жизнь.
P.S. IMHO, оба приведенных в статье примера сложны. Первый — чрезмерной абстрактностью и гибкостью, второй — слишком большим количеством условий и логики на нетипизированных данных, собранным в одном месте. Истина где-то между.
Доказывать будем от противного. Предположим, что я — не английская королева. Пусть n — число английских королев, которыми я являюсь. В нашем предположении n=0. Тогда:
n+(2+2)=0+5 (сложили два равенства)
(n+2)+2=5
n+2=5-2=3
n=3-2=1.
Откуда (по определению n) следует, что есть хотя бы одна английская королева, которой я являюсь. А значит, я и есть эта самая королева. Впрочем, все остальные королевы — тоже я.
P.S. Короны не жмут.
Примем, что 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!"))
{
....
}
Слева картинка разрастается, справа нет, но тут какой-то подвох.
Самое интересное, как всегда, происходит вдали от компьютеров. А именно: менеджер впервые после начала разработки встречается с заказчиком и наконец-то понимает, зачем тому нужна была печка. Он (менеджер) в седьмой раз приходит к программистам и говорит:
— Нам нужно, чтобы в печи можно было обжигать кирпичи.После этого надо было начать проект с нуля и сделать просто печку для кирпичей.
Ну а если проанализировать работу Бориса, то на мой взгляд он сделал две серьезные ошибки:
1. Неоправданно ввел набор сущностей Cook. Можно было сделать просто шаблонный метод в печке. Либо допилить печку до Фабрики Продуктов. Впрочем тут есть масса вариантов.
2. Перемудрил с печкой для кирпичей — тут варианта два, либо сделать кирпич таким же продуктом как и хлеб, либо вообще отдельную несвязанную печку для кирпичей сделать и отдельный кирпич не связанный с хлебом, т.к. предметная область очевидно иная и с развитием проекта расхождение требований будет только усугубляться.
Практика показывает, что если большой кусок функционала запихнуть в одну функцию/метод, то в ней сам чёрт ногу сломит (я работал с функциями по 2 тысячи строк, к концу забываешь что делалось в начале), особенно без комментариев, которыми по факту являются названия небольших функций, на которые мы можем разбить большую. А вторая проблема — у нас одинаковый по смыслу функционал раскидан в нескольких огромных методах, и если его логика меняется, то где то мы её поменять и забудем…
А вывод из статьи, подобной этой зависит от того, что гипертрафировать — засовывание всё в один метод или разбиение на сотни классов
Что в подобных ситуациях может сделать хоть менеджер, хоть заказчик?
Вывод: Борису надо срочно валить из этой конторы, пока он не превратился в Маркуса. Маркус будет работать в этой конторе, а Борис вскоре уволится, если не превратится в Маркуса.
Борису надо срочно валить из этой конторы, пока он не превратился в Маркуса.Не можете объяснить, почему именно в такой формулировке — «срочно, пока не превратился»? Есть опасность, что вскоре превратится, и это плохо?
Из примера автора понятно следующее. У заказчика возникло желание автоматизировать бизнес (или создать новый бизнес, или создать новую систему), при этом он не мог внятно донести, чего он хочет (его право, заказчик не обязан понимать, чего он хочет :-) ). Как поступил PM: ему поступала всё новая информация от заказчика, он тут же делал «гав», и, скорее всего, прямо-таки прямым текстом кидал эту новую информацию. Это не объективно, это лишь свидетельство плохой организации работы.
— убираем заказчика
— убираем манагера.
Остаются участники, которые уже не в ролевые игры играют, их успех зависит от того, как их код будет работать в реальной жизни в реальных ситуациях, и насколько быстро заработает их код.
Вроде и нужен, но реально он мало где используется. Много времени и сил тратится на UML проектирование, а шаблон полученный после автогенерации еще нужно будет много допиливать и множество других причин. UML — вроде как стандарт де-факто в проектировании приложений и во многих книгах и статьях по паттернам проектирования используются UML-диаграммы, но в кругу IT-знакомых нет людей которые бы активно применяли UML-подход (максимум раз-два в год начертили диаграмму классов или вообще помнят о UML только со времен университета).
Хотелось бы услышать ваше мнение на этот счет?
проектирование в юмл оправдано только тогда, когда естьт обратная связь из кода в юмл
UML — не нужен.
Насколько сталкивался сам, каждый раз, когда ты пилишь длиннющий UML, всё утыкается в то, что в разных языках архитектура описывается чуть-чуть по-разному. Где-то надо учитывать, что у тебя не поле, а свойство, и что свойство пишет в соответствующее поле данные не в исходном варианте, а где-то и вовсе ни пришей ни пристегни.
А красивую картинку из сырцов нареверсить всегда можно, если так уж понадобится пыль в глаза пустить.
А вот состояния, flow, по времени и т.п. — весьма неплохи, когда надо проиллюстрировать тонкие / важные места.
Оба случая встречаются в жизни. Пример первого — автоматизация крупного предприятия, без права значительно изменять бизнес-процессы. Пример второго — создание бизнеса с нуля в новой сфере.
- PMBoK — это «стиль» Бориса в переложении на управление проектами. Менеджеру, соответственно, понятнее и ближе.
- Сокращать иерархию классов сложнее (сам делал недавно «всасывание» кода с последующим упразднением классов, из которых он «всасывался»), чем усложнять — тут автоматический рефакторинг поменял на 180 градусов предстваления об обратимом и необратимом.
- Если на задачу, которую можно сделать «в одну харю», назначить троих человек, есть очень большой шанс не уложиться в сроки.
- Решение задачи усилиями в разы меньше запланированных есть проявление неуважения к менеджменту путём выставление его дураками (моего приятеля за это 10 лет назад уволили). Сложные структуры, написанные Борисом, лучше отражают масштабность и объем задач и больше тешат самолюбие менеджера.
Самая большая проблема, это вовремя перейти от стиля Маркуса к стилю Бориса. Если перейти слишком рано, когда конечная цель не видна — будет странная архитектура, а если слишком поздно, когда рефакторинг лапши становится слишком трудоемким, то архитектуры не будет совсем.
Подход Маркуса, кончено, не позволяет использовать модульное тестирование, но зато он даёт результат намного быстрее
То есть и тесты тоже не нужны? Ок, удачи )
2. Я считаю, что функциональные тесты полезнее юнит-тестов (но не отменяют их). Особенно в плане рефакторинга.
Тут две крайности, истина где-то посередине. У одного — явный over-engineering, у другого антипаттерн God Object и спагетти-код. Поддерживать сложно и то, и другое.
Нет я серьезно, никакой гений-архитектор вас не спасет при изменчивых требованиях. Изменчивые требования одна из двух главных причин провала проектов. Почитайте — Роберт Гласс. «Факты и заблуждения профессионального программирования»
В том то и дело, что архитектура меняется со временем жизни проекта, и ей тоже надо управлять. Говоря про архитектуру, я ни в коем случае не имел что-то, что надо придумать/построить/спроектировать нечто гениальное, закрыть семью печатями, и никогда не менять.
С этим не поспоришь. Однако на ходу архитектуру менять невозможно, а требования могут меняться.
Например проект разрабатывался в течении 5 лет, огромное количество времени ушло на тестирование (тестирование верстки, функционала, нагрузочное тестирование и т.п.) и отладку, и сейчас все работает стабильно. И тут заказчики хотят новую фичу. Фича никак не ложиться на архитектуру проекта, 5 лет назад не кто не мог предположить что она понадобиться. И еще нам конечно нужна обратная совместимость. И что вы предлагаете, перелопачивать архитетуру ради этой фичи? А потом еще год тратить на отладку тестирование и стабилизацию? Ну да архитектура будет «правильной», но какой ценой! Никакой зказчик на это не пойдет, да и разумный разработчик тоже. Соотвественно старый код не трогается, а новая фича дописывается где-то сбоку, при этом нам нужно будет только отладитть и протестить новый код, т.к. старый почти совсем не застронут. А как это выглядит с точки зрения архитектуры? — естественно это кастыль. И в реальных долгоиграющих проектов таких костылей великое множество. Чем больше таких изменчивых требований тем больше костылей, и в какой-то момент все это чудо может начать сыпаться.
>> придётся чуть поменять логику метода createBread
А там кровь, кишки, расчлененка. И уже не чуть менять надо…
На каждое такое серьёзное усложнение задачи он должен отвечать, что это будет целиком новый проект, который и оплачиваться должен соответственно. И начинать его надо с нуля, и тогда таких вавилонских башен городить не нужно.
> код, написанный два-три года назад, превращает меня в эмо: хочется заплакать и умереть
Мне хочется его переписать.
> «Хм», — произносит Борис и вспоминает про шаблон «строитель»
Да вы издеваетесь! Сколько можно?
Спасибо автору.
Весь день откладывал статью от чтения и не пожалел, что наконец-то уделил время. Какой это типический сюжет.
Думаю, завтра прибежит менеджер и сообщит: дико извиняюсь ребята, но как выяснил мне подсунули не совсем того заказчика, его уже продали другому вендору, а мы должны были разрабатывать по для моделирования новых образцов хлебобулочных изделий… Со сроками мы уже не укладываемся, так-что придется работать сверхурочно. Кто сделает быстрее всех, тому почетную грамоту…
Разбор полетов:
Тот, кто писал кучу классов работал честно и самоотверженно, второй представляет собой настоящего профессионала и гораздо правильней поступал. Теперь самое интересное как мне показалось. Первый пытался моделировать процессы и реальные объекты описывающую задачу, но на самом деле он моделировал выжимки непонятных идей от менеджера.
Второй, более опытный и понимает, что все это шатко, задание не устоялось, сто раз все поменяется, поэтому моделировал только объективные факты предметной области, не торопясь забегать вперед. Словом, делал, то в чем есть реальная необходимость. В результате сделал работающую модель адекватную заданию менеджера в отличие от первого который моделировал «плавный взрыв» его мозга ему самому… При этом, он не наплодил лишних сущностей и не растратил зря силы. Думаю завтра он напишет «моделлер» булок уже к обеду, закусывая круассаном.
Конечно его код несовершенен, но проще доработать чем в буквальном смысле слова переработать. Тем более что вероятность ошибок у данного разработчике ниже, чем у первого.
Не совсем верно. Он моделировал свои представления о процессах и объектах. Задача была «чтоб делался хлеб». Моделировал бы он реальные процессы и объекты у него не появился бы на первой же итерации абстрактный класс Product и ещё более абстрактная фабрика ProductFactory.
Второй… моделировал только объективные факты предметной области, не торопясь забегать вперед.
Тоже не совсем верно. Скажем, когда он ввёл параметр bredType:int, то тоже отошёл от объективных факторов. Объективные факты были хлеб, пирожки с мясом, пирожки и капустой и торты, а не номер типа.
Лучше так: Он пытался по настоящему моделировать процессы и реальные объекты описывающую задачу, но в результате постоянно получалось моделировать свои представления о процессах и объектах под соусом постоянно меняющихся искаженных представлений о задаче менеджера )))
на первой же итерации абстрактный класс Product и ещё более абстрактная фабрика ProductFactory
Там вроде фабричный метод у него был.
Скажем, когда он ввёл параметр bredType:int, то тоже отошёл от объективных факторов Ну он же программист: взял самое простое решение формализации вместо каталога паттернов ))) Это кстати правильно на первых этапах. Художник, скульптор начинает работу «грубыми мазками» еще нет четких очертаний. Второй тоже так-же сделал: «тупо воткнул параметр» не мудрствуя…
Объективные факты были хлеб
Согласен, но речь идет конечно об простейшей формализации сущностей. Самое главное, что он вычленил — это сразу сделал работающий метод с входом и выходом, дающий сразу результат, а не набор кучи классов. Причем это сделал исходя только из уровня информированности на данном этапе.
Простейшая она с точки зрения реализации, и то не факт. Была у него необходимость сводить вид сущностей к целому числу, абсолютно нейтральному семантически? Пускай он даже в теле метода использует константы или перечисления, но сигнатура метода нам ни о чем почти не говорит. Сделал бы перечислимый тип — было бы лучше.
в сторону Си и смотреть не хочуВаш поход — это ещё одна крайность. Я вас ни в коем случае не осуждаю, если вы делаете это просто для души. Но я бы не советовал применять такой подход для более-менее серьёзных разработок для МК: кода получается больше, его труднее читать, дольше писать (приходится вручную писать тривиальные для C вещи), а о портируемости даже железно-независимой части кода сразу можно забыть. Ну и всё зависит от решаемых задач: если нужно не просто поиграться с МК, а сделать полезное устройство, лучше воспользоваться библиотеками на C и решить задачу быстро и эффективно, чем писать всё с нуля на асме и решить задачу только эффективно (:
Отправил случайно (:
Впрочем, я сейчас just for fun пишу компилятор языка — этакой замены C: низкоуровневый, как С, с возможностью линковки и вызова сишного кода, но также с возможностями метапрограммирования и фиксированными размерами целых типов на всех платформах (для начала сойдёт). Сдаётся мне, что я его доведу до юзабельного состояния раньше, чем мой любимый D станет доступен на Cortex-M3.
Он [Борис] создаёт класс Recipe, а к нему — строитель RecipeBuilder. Рецепт он внедряет (ВНЕЗАПНО!) в печку с помощью сеттера setRecipe(recipe:Recipe).
А Маркус (вы не поверите) добавляет ещё один целочисленный параметр в createBread — recipe.
Это пять! Рецепт ну никак не может быть меньше объекта с шагами и ингредиентами. Это же рецепт! А у Маркуса это просто int? Integer? Целое число? Это же что за бомба там в реализации? Куда Маркус захардкодил все остальные данные? Неужели в createBread? Всё это автор скрыл, оставив только одобрительное «вы не поверите».
А что сделал Борис? Он сделал объект. Правильно сделал. Но чтобы показать неправильность кучи классов, автор додумал ошибку с ненужным билдером, и setRecipe, хотя вполне можно было бы передавать рецепт в cook(), как это сделал Маркус, и вообще было бы зашибись. Какой плохой Борис!
Итого: реализацию рецептов Бориса надо просто чуть-чуть улучшить, а жесть от Маркуса надо вычищать веником вместе с Маркусом! Но из статьи выходит, что Маркус молодец. Не зачёт.
Да, команды, разумеется, это просто целые числа. Без символических имен. Зачем? Автор и так их прекрасно помнит, а если забыл — достаточно посмотреть в соответствующий case.
Вспомнился один из топиков до этого с таким же примером.
А вот что удивило — почему никто из отписавшихся не вспомнил что надо сохранять исходники? ;)
Вообще кто-то задумывается о сохранении исходников? Ведь в результатах на много сложнее всем потом разбираться.
| Борис абсолютно безосновательно считает, что источник газа может быть только один.
Вот он исходник. И это очень важное принятие решения. Которое… все выкинули и забыли. А надо бы большими буквами «причина возникновения этого класса в том что мной было принято допущение .....».
То же самое о требованиях менеджера — прописать везде «этот класс является прямым следствием исходного требования норме 123 » текст требования ...".
А потом вдруг выясняем что хлеб нам печь не надо и мы раз… выбираем все что возникло на основании этого требования и не имеет более исходных зависимостей и разом удаляем ;)
Или у нас есть исходное требование — «поддержка разных платформ». Мы его вносим во что-то (не голову, не документаци и не в ТЗ) в программе/среде разработки. И добавление какой угодно плюшки одной платформы тут же вызывает ошибку не соответствия требованиям. Даже если менеджер ее просит добавить — его требование тут же вызывает ошибку… которую… надо решать меняя или удаляя ранее установленные требования.
А вы все эти исходные требования выкидываете как мусор, а потом за голову хватаетесь — как же нам поддерживать эти «бинарники» из кода C++ без «исходников» — требований :)))
То, что у Бориса много квадратиков, а у Маркуса — мало, ничего не означает. Помимо отвлеченной схемы классов автору следовало привести то, что внутри. У Маркуса там 100% адский ад.
Почему-то в последнее время стало модным отрицать заповеди Макконнелла и Фаулера. Это очень удобно, не надо развиваться, изучать теорию, исписывать 100500 страниц в блокноте в попытке найти красивое решение. Зачем? Можно дать себе индульгенцию на говнокод, оправдываясь меняющимися требованиями или погодой или тем, что «надо было срочно». Фигня всё это. Разработчик либо рефлексирует, либо пишет код в стиле дамп потока сознания, как Маркус.
В принципе функциональный подход можно применять практически в любом языке.Да, и обычно это называется «шаблоны проектирования».
Так что если вы будете применять функциональный подход в языке более низкого уровня, у вас — внезапно — будут — сами собой — вырисовываться «шаблоны проектирования».
можно пример?
- С++, как «объектно-ориентированный язык» — это вообще «первый блин комом». Интерфейсов в нём нет, есть только множестенное наследование от классов.
- Если есть реализация, встроенная в язык, то а) зачем делать другие реализации б) зачем называть её термином не из языка, а из книжки про паттерны. И я интерфейс стратегией не называл, я и назвал его — «встроенной реализацией шаблона».
- Про IDE — так на Java набор кода без неё — это баловство, а не серьёзное программирование. Качественного кода без рефакторинга и форматирования получить нельзя, а средствами IDE это делается в сотни раз быстрее, и занимает много меньше времени, чем набор кода. С другой стороны, нужен ли качественный код на таком «write-only» языке как С++ — тоже вопрос. Иногда нужен, даже ценой замедления разработки раз в 10.
Ага, совсем чуть-чуть. :)
Не очень понятен вывод автора про то, что код Маркуса не может быть покрыт тестами. Не вижу ниодной принципиальной проблемы, которая бы мешала сделать это.
Еще замечание на счет Маркуса. Почему-то все комментирующие решили, что весь код Маркуса должен быть собран в методе сreateBread().
Подобные схемы отображают структуру приложения на конкретном уровне абстракции. Более низкие уровни на таких схемах просто не отображаются. В данном случае обе схемы отображают интерфейсы классов и их взаимодействие. Внутреннего устройства классов на них нет. Поэтому нет оснований считать у код Маркуса собран в одном методе.
Код легко разносится по приватным методам класса. Да и просто может быть вынесен во внешние модули или может использовать сторонние библиотеки, которые на верхнем уровне абстракции не отображаются, поскольку не имеют принципиального значения для архитектуры приложения…
Поэтому считаю, что Маркус таки да… Может считаться примером для подражания… ))
На практике у Маркуса если он будет и дальше следовать такому подходу, не отклоняясь, количество параметров в createBred() начнет сильно расти и метод будет управлятся их хитрой комбинацией. При этом все клиенты этого метода должны быть проверены при его изменении.
Плюс на выходе непонятно что, то-ли хлеб, то-ли кирпич. А если где то нужно отделить пирожки (оба типа) от хлеба то это прямо в коде скорее всего будет, хардкорно, по id и при добавлении нового вида пирожков, будем по коду рыскать. Документация к этому методу будет занимать страницы, а значит никогда не будет актуальной.
Внутренний код метода (а после и класса) запутан и через год активного развития проекта только маркус там что-то сможет править. А если в команде 10 маркусов — то никто, без риска повалить весь проект (если класс критичный).
Борис конечно тоже перестарался (хотя тут по обстоятельствам смотреть надо) — но у него где-то в ближайшем будущем, когда модель начнет ломатся будет рефакторинг.
У маркуса его не будет (не его метод, ведь он выполняет задачу, а при рефакторинге он превратится в бориса), будет полное переписывание.
Хотя перегиб Бориса более опасен чем маркуса, но пока я тут особых перегибов не вижу. Хуже всего конечно, если счас а кодом бориса начнет работать маркус.
В статье конечно все сильно утрировано, идеал где-то между ними, в зависимости от проекта, команды и функционала.
Обратите внимание: Борис принял о выделении кирпича в отдельный класс не исходя из требований задачи/менеджера, а исходя своих знаний о реальном мире. В реале кирпич сильно отличается от хлеба. Хлеб едят, а кирпич нет… Но какое это имеет значение к контексту задачи, не понятно. В контексте задачи в конечном счете может оказаться важным, что хлеб и кирпич оба являются брусками, имеющими параметры длину*ширину*высоту. В этом случае разделение сущностей не обосновано.
Поэтому я считаю, что Маркус совершенно правильно сделал, что не стал городить лишних сущностей. Задача на том этапе этого не требовала.
Тут выше уже говорили, добавить лишний класс и поменять тип параметра у createBread Маркуса — не проблема. А вот убрать избыточные сущности уже задействованные в структуре приложения, это реально будет геморрой. Борису придется рефакторить до посинения.
Главный критерий в данном случае — это эффективность программиста, сколько времени он потратил на реализацию затребованного функционала. Прикиньте сколько времени потратили Борис и Маркус на реализацию своих вариантов решений. А теперь представьте себе, что Борис и Маркус работают на почасовой оплате…
Обе реализации архитектуры приложения выглядят криво, но постановка задачи, извините, еще хуже… Но иначе получится не могло. Каков вопрос, таков ответ…
— преждевременное абстрагирование (на самой первой итерации создал класс Product. от которого унаследовал Bred и так постоянно)
— необоснованное применение знаний реального мира (ввёл повара, кирпич отличил от хлеба, а пирожки с тортом сделал счёл однотипными с хлебом сущностями, хотя возможно как раз кирипчи с хлебом в данном контексте однотипные (монолитны), пирожки другой тип (требуют начинки перед выпеканием), а торт совсем другая — требует «наичинки» после выпекания)
— злоупотребление паттернами до момента когда их необходимо использовать
http://okertanov.github.com/2012/10/04/Programmers-and-bread/
Наконец-то, спустя девять лет, пелена тайны может быть сброшена! Вы можете смело рассказать, кто был прообразом Бориса.
Не вкусно! )
Если, конечно, там на одного Маркуса целый отдел QA, то они конечно добьются требуемых потребительских качеств. Но в реальности, любой адекватный руководитель заставит такого «разработчика» самого писать тесты на этот код. И тогда диаграмма компонентов (вместе с немаленьким самопальным фреймворком тестирования) будет куда как печальнее
Кому доводилось рефакторить методы из тысяч строк — меня понимает)
- Не могу копать
- Могу копать
- Могу не копать
Могу допату так засунуть, что фиг кто найдёт
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);
}
В остальном, Борису не мешало бы убить в себе перфекциониста, а Маркусу — всё же почитать про паттерны и тестирование кода.
Интересно, автор сознательно применил набор манипуляций или это получилось само собой?
А то все статья, принципы сравнения и даже поведения программистов такие, что бы Маркус выглядит максимально хорошо на фоне Бориса. Просто автор даже специально избегает создания перечислений и указания приватных методов, что бы Макрус выглядил прям очень хорошо на фоне бедняги Бориса.
А в реальности, если кто-то использует инты вместо перечислений или объектов, то это конечно, сразу красный маркер.
Как два программиста хлеб пекли