Repost from habr.
Всеволод Леонов – менеджер по продуктам, «Embarcadero».
Александр Люлин – ведущий разработчик, «Гарант»
Максим Крылов – руководитель проекта, «Гарант»
Внимание, информация в следующем абзаце абсолютно не рекламного характера. Никто из авторов не будет обсуждать и даже упоминать какие-либо программные продукты в контексте их свойств, функций, конкурентных преимуществ, эмоционального восприятия, появляющегося в голове от многократного произнесения названий т.д. Ниже будет представлено упоминание продукта и компании для определения квалификации приглашенных экспертов. Проще говоря, кому интересно читать мнение об UML некого «васи пупкина», который разработал программу «бест-ххх-даулоадер-иксплойт»?
Компания «Гарант» известна многим российским IT-специалистам благодаря своему ключевому продукту – системе ИПО ГАРАНТ. Большая часть сотрудников компании заняты производством ПО, а сама система имеет 23-летнюю историю развития и насчитывает десятки миллионов строк кода, написанных на различных языках программирования. При таком масштабе, языковой и технологической неоднородностях, высоких темпах производства, очень жёстких требованиях к стабильности только применение самых передовых технологий может обеспечить качество эволюционирующей системы. UML как средство моделирования, бесспорно, является одним из таких «продвинутых» подходов, применение которого в компании «Гарант» отличается высоким уровнем автоматизации со значительной долей усиления его системной роли. Сегодня своим опытом делятся ведущий разработчик системы ГАРАНТ Александр Люлин и руководитель проекта Максим Крылов.
Доказательство отсутствия рекламы — давайте разберём, что написано выше. Мы только что определили: название компании, сотрудниками которой являются эксперты; сайт компании; профиль компании; какой продукт производит компания; технические параметры продукта (количественные и качественные), позволяющий оценить масштаб разработки; степень «проникновения» UML в процесс производства ПО.
Сайт компании приведён опять же не из рекламных соображений. Так делает даже wikipedia, которая является эталоном «безрекламности». Указанный выше абзац служит для придания публикации а) полноты; б) ответственности перед читателями.
Если вы (в широком смысле — люди, компании, специалисты, эксперты) не согласны с авторами, рады будем увидеть ссылки на ваши публикации по сабжу. Если нужно в этом помочь — обращайтесь vsevolod.leonov@embarcadero.com.
Всеволод: Расскажите, был ли изначально использование UML принято в качестве одной из составляющей процесса разработки?
Максим: Нет, конечно, когда мы пришли в «Гарант», его еще попросту не было. Но, кажется, уже в 97-м году наш молодой и талантливый коллега (хотя в ту пору мы все были молоды) принёс дискету с одним из первых инструментов UML-моделирования. Примерно с этого момента можно считать, что внедрение UML в стенах «Гаранта» началось. Однако, до его использования в основных наших проектах, так сказать в промышленных масштабах, прошли еще годы.
Александр: Более того, использование UML внедрялось долго и в несколько этапов. По мере роста наших разработок и понимания, что без этого инструмента есть риск не справиться с всё усложняющейся структурой кода и внутренней архитектурой проектов.
Всеволод: Не было ли «идеологических разногласий»? Есть разные стили кодирования, не получалось ли так, что «у каждого свой UML»? Или UML «причёсывает всех под одну гребёнку»?
Александр: Конечно, были разногласия. И как раз в идеологической основе внедрения и было стремление все «причесать» и унифицировать имеющиеся подходы.
Максим: К тому моменту, когда использование UML из экспериментов одного-двух энтузиастов перешло в стадию проектного внедрения, у нас уже было довольно четкое понимание что это и зачем и как сделать так, чтобы не было «разных UML-ей».
Всеволод: В какой момент было принято решение посмотреть в сторону UML?
Александр: Когда появилось осознание того, что проекты сложные. Что в них задействовано много людей. Что надо как-то «договариваться». И что надо как-то «видеть» общую архитектуру проектов.
Максим: Долгое время его использование ограничивалось личными набросками для потребления узкой группой «посвященных». В какой-то момент пытались начать генерировать из него CORBA IDL. В итоге, пришли к выводу, что это почти невозможно и написали свой простенький генератор. Собственно, это и было точкой невозврата. После этого UML стал применяться в большинстве проектов, и что главное, переродился в нечто существенно большее, чем просто набор картинок, мы об этом потом чуть подробнее расскажем. Но в начале, да — просто как возможность быстрее и эффективнее договориться.
Всеволод: Были ли случаи, когда договориться не удалось даже с UML?
Максим: В любых коллективах возникают ситуации, когда кто-то теряет желание договариваться. Тут уже не поможет ни UML, ни что-то другое. Но когда люди занимают конструктивную позицию, то наличие универсального языка очень облегчает жизнь.
Александр: UML – не догма и не «универсальная пилюля», а средство. Но работать стало реально лучше. Потому, что стало возможным просто «скользить взглядом» по всей системе, быстро меняя масштаб рассмотрения. То мы смотрим на UseCase, то мы через 5 минут уже спустились «к байтам». Масштабирование очень быстро делается. Скажем, как в Google Earth.
Всеволод: Не является ли использование подобных техник и просто необходимость «договариваться» индикатором того, что есть проблемы в распределении «зон ответственности» в коде? Зачем договариваться и согласовывать, если можно чётко разграничить, кто какие классы/методы разрабатывает, а кто их использует? Я делаю болты, Вася – гайки, а Жора свинчивает ими две детали. Какие детали? Да без разницы, мы чётко поделили зоны ответственности. Почему это в области разработки ПО не работает?
Александр: До какого-то момента у нас и такая схема работала. Но пришлось меняться ролями. Замещать других. Да и сложность проектов возросла.
Всеволод: А как понять, что проект уже стал «сложным»?
Максим: Все зависит от количества цепочек «болт-гайка-деталь», от многогранности каждой гайки, от вариативности болтов и манипуляций с ними. Чтобы написать «hello, world» не нужно ни с кем договариваться и делить зоны ответственности. Не нужен UML и OOP, SOAP, XML и «гибкие» методологии. Все начинается с ростом проекта. В какой-то момент оказывается, что Вася изменил диаметр резьбы у своих гаек, и Жора больше не может навернуть их на ваш болт, а еще хуже они с них сами соскакивают, уже где-то потом, дальше. Или вдруг выясняется, что в конце цепочки стоит новенькая Маша, которая с неимоверным трудом раскручивает обратно ваши гайки с болтами, и меняет их на заклепки, которые идут из соседнего цеха. Это все примитивные примеры, но накапливаясь, перемешиваясь, образуя причудливые комбинации, они и создают сложность, именно из-за них мы и теряем качество, как процесса, так и результата.
Использование любой нотации, понятной всем участникам процесса, уменьшает количество подобных сюрпризов. Гипотетическое производство с болтами и гайками было бы невозможно без чертежей и документации. Но этого тоже не достаточно. Чтобы построить эффективный конвейер вам нужны станки, роботы с ЧПУ. Они однозначно и без вольностей трактуют входную документацию и выдают гарантированный результат. Поэтому один UML не приносит столько пользы, сколько UML который вы можете скормить «станку» и получить на выходе 100% валидный, отлаженный код, который затем уже в ручном режиме превратите в финальный продукт.
Всеволод: Как происходит процесс моделирования? Прямо так собирается группа «заинтересованных товарищей» и все одновременно «тычут пальцами в диаграмму» и дорисовывают её? Или есть чёткое разграничение – ведущий-ведомые. Т.е. один рисует – остальные принимают к сведению?
Александр: Бывает и такое. И собираются, и тычут. Разделение ведущий-ведомый — тоже случается. Но это скорее «роли», а не должности. «Круг специалистов» начальный – какой-то есть. Сначала общаемся устно. Потом читаем требования. Потом задаём по ним вопросы. Получаем на них ответы. Рисуем прототип. Где-то тут же определяем «круг специалистов». Более узкий. Или более широкий. «Обкатываем» решения. Находим «подводные камни в ТЗ» или противоречия с «существующей архитектурой». Далее, процесс, в целом, циклический.
Максим: У нас, как правило, рисуют все, ну или почти все. Зависит от проекта и специфики, конечно. Но теоретически, модель «ведущие» (именно «ие») — «ведомые», мне кажется более правильной и жизнеспособной, т.к. позволяет наиболее оптимально использовать сильные стороны одних и нивелировать недостатки других. Те, кто лучше «заточен» на проектирование, рисуют модели. Те, кто увереннее чувствует себя в реализации алгоритмов, генерирует из них код и наполняет его бизнесс-логикой.
Всеволод: Какие проблемы изначально хотелось решить с помощью UML?
Максим: Изначально еще на уровне эксперементов, просто как способ структурировать свои мысли. Самые первые опыты включали лишь рисование классов и связей, в этом не было четко сформулированной практической пользы, скорее нам это нравилось эстетически, это было здорово и интересно, будто мы рисовали иллюстрации к книгам «классиков». Но уже к моменту реального внедрения, основной проблемой, которую хотелось решить это недостаточная скорость и качество ручного кодирования «рутинного» кода. Т.е. во главе угла стояла кодогенерация.
Александр: Сложность взяимосвязей. Их «неощущаемость» в «голом» коде. Правила проектирования и кодирования. Использование шаблонных решений. Опять же UML рассматривалась как возможность «договорится» более формально.
Всеволод: И почему именно UML?
Максим: В то время альтернатив просто не было, UML был единственной универсальной графической нотацией достаточно целостно описывающей предметную область и имеющей инструментальную поддержку, даже с какой-то кодогенерацией. Поэтому можно сказать, что так сложилось исторически. А позже, когда мы поняли, что не так важно «как» рисовать, сколько «что и зачем», то менять UML на что-то другое (например, DOT) просто не было смысла. По большому счету, UML для нас лишь конкретная нотация знакомая большинству, «форма стрелочек и квадратиков», важен не она, а принцип действия, т.е. то, что мы с ним делаем и как.
Всеволод: Классики не обманули? Или у вас образовалось своё видение места и роли UML?
Максим: Классики задали базис, а мы его использовали. И использовали так, как классики подразумевали, как нам кажется, но не до конца сформулировали. Во-первых, не было определено сквозное проникновение модели. Да, все слышали про MDA, но много ли его видели? Мы это «исправили», например, одна из наших версий метамодели подразумевала начало дизайна системы с описания «проблем» на UML, которые она должна решить, из них должны были рождаться «возможности», из них – «прецеденты» и т.д. до «сервантов», до «байтов». Всё это было одной целостной моделью, в которой можно было про каждый «байт» сказать, какую пользовательскую проблему он решает. А для каждой «проблемы» – получить весь исходный код с ней связанный.
Александр: Это подобие «матрёшки» и «сборочных чертежей». Причём на разных уровнях, которых может быть сколь угодно много, а не просто Class View, Deployment View, как у «классиков». Чертёж «уровня предприятия» и чертёж «уровня контейнеров в стиле STL». UML позволяет «логарифмировать» сложность задачи. Для меня лично проектирование/кодирование/отладка/тестирование достаточно большого UseCase – не сильно сложнее отладки «списка целых». Другое дело, что и отладка «списка целых» – иногда может занимать и месяцы. А так – «сборочные чертежи», матрёшка и «номенклатура микросхем». Это – краеугольные камни. Они с одной стороны – «логарифмируют» сложность, а с другой стороны – позволяют «быстро менять масштаб» рассмотрения системы. И ещё, «классики» не уделили должного внимания <>. А у нас это – краеугольный камень. <> – это как раз элемент архитектуры и мета-модели. Стереотип влияет на способы кодогенерации и на конечный код.
Всеволод: Обсудим понятие <>?
Максим: Основная проблема использования UML в «классических» инструментах в том что, кодогенерация жестко связана с предопределенной статической метамоделью. Вы не можете изменить ни одно, ни другое. Ни изменить, ни расширить, ни поменять правила или задать новые специфические. Главное, что классики не раскрыли широкой публике, а «классические инструменты» не реализовали, это потенциал UML именно с точки зрения мета-конструирования. Создания собственных метамоделей со своей специфической кодгенерацией. И вот тут ключевую роль начинает играть понятие <>, о котором говорит Александр. Стереотип позволяет нам определить группу специфических метаклассов с привязкой к ним произвольной кодогенерации. Фактически, с помощью стереотипов, вы формируете произвольную мета-модель, которая автоматически становится вашим собственным DSL, трансформирующим квадратики в код, по любым правилам которые вы реализуете. Именно этот аспект позволил UML стать для нас не просто инструментом для рисования картинок, а тем, что реально упрощает процесс разработки, иногда делая поистине невозможное – возможным. И как нам кажется, отсутствие этого или аналогичного механизма в коммерческих инструментах, и приводит сейчас к тому, что UML перестают использовать и смотрят на него как на «пятую ногу».
Александр: Добавлю, в UML есть собственная «мета-модель», нарисованная на самом же UML. В ней есть понятие «класс элемента». Это – Class, Category, Operation, Attribute, Dependency, Port, UseCase, Actor и т.д. Из этих классов строятся «конечные диаграммы». И кодогенерацию обычно привязывают как раз к классу. Но в этом и ошибка у «классиков» — нет ни гибкости, ни расширяемости. Мы же поднялись как бы на уровень выше. Собственно UML описывает у нас мета-мета-модель, т.е. правила формирования правил. Дальше мы определяем мета-модель, вводя в нее любые нужные нам понятия. Например, мы можем определить для Delphi возможность множественного наследования. Затем привязываем к этой метамодели правила преобразования ее элементов в код (или любые другие артефакты, например в документацию, вспомогательные файлы и т.д.). В примере с множественным наследованием это может быть трансформацией в одиночное наследование плюс агрегацию, но при этом так, что с точки зрения программиста это будет выглядеть именно как 100% наследование. Наконец, мы создаем реальные модели своей предметной области, но уже оперируя не тем, что нам изначально предложили «классики», а всем тем арсеналом, что мы сами придумали на мета-уровне и получаем из них готовый код произвольной сложности.
Всеволод: Расскажите детально, как это всё у вас развивалось – ступенчато?
Максим: В начале работ над новой версией «Гаранта» («Платформа Ф1»), перед нами встала проблема: сервер разрабатывался на C++/CORBA, а клиент – оболочка на Delphi. По определенным причинам использовать на оболочке, родной для Delphi, VisiBroker, мы не могли, а для серверной части вменяемой альтернативы мы не видели. Тогда было предложено решение, завести на клиенте «адаптер» (dll на C++), который бы работал с сервером, используя CORBA, дополнительно проводя какие-то внутренние преобразования, кеширование и даже содержа некоторую часть клиентской логики, отдавая при этом все, что нужно оболочке на Delphi в понятном и удобном для нее виде. Думаю, не стоит говорить, что у такой системы как «Гарант», номенклатура интерфейсов, через которые взаимодействовали адаптер и оболочка, быстро приобрела угрожающие размеры. А теперь представьте, что собой представляет любой объектный интерфейс, между dll на C++ и delphi? Если кратко то это «сущий ад»: разные правила передачи типов, разное управление памятью, отсутствие контроля описания в экспортируемом header-е и в Delphi, когда абсолютно любое несоответствие приводит к совершенно непредсказуемым результатам и сверх-нетривиальной отладке и т.д.
С учетом, того количества интерфейсов, которое были и еще обещало быть, а так же степени их изменчивости, мы быстро поняли, что задача по сути не выполнима. Вот собственно в этот момент мы впервые и использовали UML «по назначению». Свой генератор для CORBA IDL у нас уже был, и он уже был построен по принципу шаблонов, описывающих метамодель и кодогенерацию на ее основе. Собственно все, что нужно было это – определить новую метамодель для интерфейсов адаптера и описать на ее основе кодогенерацию, на выходе которой получались бы на 100% согласованные описании как для C++, так и для Delphi, с учетом всех особенностей и тонкостей.
В итоге, все было сделано достаточно быстро, и начло приносить результаты. Программисты на С++, которые писали адаптер, больше не задумывались вообще о том, что что-то, куда-то нужно экспортировать Программисты на Delphi, работали с адаптером так, как будто он был написан полностью на Delphi, по привычным им принципам вплоть до именования методов и свойств. Все просто рисовали на модели нужные интерфейсы, генерировали код и работали «нативным» образом для обоих языков. По большому счету, это было первой sucсess story, после которой мы активно начали развивать идею шаблонной генерации и метамоделирования. И во многих направлениях (хоть и не во всех) добились впечатляющих результатов.
Всеволод: Как происходит вовлечение нового человека в команду разработчиков? Ему дают «под нос» ворох диаграмм? Или начинает он с элементарных операций, не требующих «видеть систему в целом»?
Александр: Есть список неключевых или второстепенных возможностей. Для начала – даём их. Но не очень много. Чтобы не «отбить охоту» рутиной. Смотрим, как втягивается. Попутно рассказываем про «инфраструктуру» и собственные компоненты. Консультируем устно и письменно. Есть ещё набор документации, которую рекомендуем читать. Потом, постепенно, «настоящие» задачи, модель и всё остальное. Как-то так. Если бывают на повестке дня «отдельно стоящие большие задачи», то и с них начинаем.
Всеволод: Всем известно, что новые подходы и методы не всегда положительно воспринимаются всеми участниками команды. Были ли случаи активного или пассивного противодействия?
Максим: Многие восприняли «наш UML» как вторжение в их личную жизнь, ограничение свободы действия. Ведь до этого каждый программировал так, как хотел. Тут появились какие-то правила и ограничения даже на уровне проектирования реализации, а не только интерфейсов. Возможно, мы немного перегибали палку и вводили излишне много ограничений. А ведь это очень мощный инструмент, вы не просто определяете метамодель и кодогенерацию, вы можете еще и описывать произвольные «констрейнты», связанные с ней. Например, от «нельзя передавать коллекции в качестве результата по ссылке», или «у метода не должно быть больше 5 параметров», до контроля архитектурных слоев, и «трассируемости» в требования. Разумеется, далеко не все были согласны со всеми ограничениями, многих из них это и напугало и оттолкнуло.
Всеволод: Как проходил процесс внедрения – выполнялось ли обратное проектирование?
Александр: Не выполнялось. В тот момент просто не было хороших инструментов. А сейчас я пришёл к глубокому убеждению, что это и не надо. Т.к. в процессе «рисования уже существующего кода» происходит его переосмысление, появляются идеи и пути его рефакторинга (не стоит только делать рефакоринг в процессе рисования, он должен идти отдельным этапом), а также применяются шаблоны проектирования/кодирования. Никакой «автомат» это не сделает. Все попытки перенести что-то на модель всякого рода «автоматами» выглядели удручающе.
Максим: Уже на этапе создания шаблонного генератора нам стало ясно: автоматический реверс-инжениринг – «зло». И не потому, что провести его на достаточно качественном уровне практически не возможно, даже если у вас примитивная базовая метамодель, содержащая только родные для языка абстракции, не говоря уж о более сложных специфических метамоделях для которых, думаю, сделать автоматический реверс невозможно даже теоретически. Ключевой момент был в другом – наличие реверса ломает саму идею использования UML для кодогенерации. Нашей задачей было научиться самим и научить других думать не в кодах, а в абстракциях, по возможности к коду не привязанных. Это – отдельная сложная тема, и это было крайне важно для нас. Поэтому, наш генератор по своей архитектуре и по тому, как он генерировал код, исключал и одновременно делал ненужной возможность реверса, как таковую.
Всеволод: Сделаем шаг «назад» к ООП. Можно ли сказать, что UML способствует рефакторингу?
Александр: Да. Способствует. Но, могу сказать, что не стоит смешивать рефакторинг с «реализацией требований». Их надо «разносить во времени». И думать о тестах.
Максим: Опять же смотря «какой» UML. Если вы просто рисуете «мертвые» диаграммы, то это не очень поможет, хотя может и даст небольшой положительный эффект. Если у вас есть метамодель и шаблонная кодогенерация, как в нашем случае, то возможности по рефакторингу оказываются на совершенно новом уровне.
Всеволод: Почему приходится делать рефакторинг объектного кода? Это – недостаток опыта? Или объективные причины, обусловленные эволюцией системы в целом? Или просто естественный «рост в ширину и глубины» системы?
Александр: Думаю, тут имеют место все перечисленные причины. Рефакторинг — это отдельная большая тема. Но рефакторингу помогает пара – модель + тесты.
Модель ограничивает разработчика «сверху», позволяя оставаться «в рамках Архитектуры» (те метамодели) или грамотно модифицировать её. А тесты ограничивают разработчика «снизу» позволяя ему остаться в рамках ТЗ и регресса уже найденных ошибок. Т.е. модель, скажем так, – «тьютор», а тесты – «валидатор». Хотя есть коллеги, которым я сказал именно эту фразу, и они восприняли это со скепсисом. Говоря что-то вроде «никто не запретит криворукому программисту сделать во View бизнес-логику, а в бизнес-логике – сделать элементы View». И они – правы. Причём обычно скептиками являются как раз «пряморукие» программисты.
Почему нужен рефакторинг? Ни для кого не секрет, что вся IT-индустрия во всём мире так и не научилась «вменяемо» планировать сроки разработки. А коли так, сроки начинают поджимать. Какой бы грамотный анализ на начальном этапе ни был сделан, жизнь оказывается сложнее. А дальше решения начинают приниматься «в стиле XP». Что на мой взгляд, в общем, правильно. «Я подумаю об этом завтра», – говорила одна героиня. И до поры до времени эти решения «имеют право на жизнь».
Часто бывает так, что требования к уже существующей системе, изменяются «взрывоподобно» или «лавинообразно». И им становится «тесно» в рамках архитектуры, которая хотя и «гибкая», но не «резиновая». Тут тоже – надо делать рефакторинг. Чтобы архитектура «не развалилась».
Всеволод: И насколько вам стало проще «рефакторить», со всем этим набором UML, DSL, кодогенерация?
Максим: Вот реальный пример из жизни. У нас на метамодели были определены фабрики интерфейсов — метод на интерфейсе со специальным стереотипом, который с помощью шаблона превращается в коде в набор других интерфейсов и классов, реализующих несколько разных паттернов, что в итоге позволяет довольно гибко управлять в рантайм связыванием реализации с интерфейсами, кешировать их, вытеснять и т.д. Повторю, все это – один метод, нарисованный на модели. В какой-то момент, когда это использовалось уже пару лет, и таких фабрик был не десяток и не два, мы неожиданно поняли, что в коде есть неоптимальность, влияющая на производительность. Но чтобы ее устранить, нужно изменить структуру этих генерируемых классов. Не просто заменить «a» на «b» поиском по всему проекту, а именно поменять структуру. В нашем случае это заняло несколько часов для того, чтобы переписать шаблон генерации и перегенировать все проекты. А если бы все это было написано вручную, то в сотне разных мест были бы «распиханы» реализации этих нескольких паттернов, везде немного отличающиеся, и поменять, а потом отладить все это – было бы не реально ни за два часа, ни за два месяца.
Александр: Кроме того, мне проще «рефакторить» диаграмму, а не код. Проще переносить с места на место «квадратики» на модели, нежели куски кода и файлы между папками. При этом ещё «руками» надо правильно «сохранять архитектурные слои». А модель – «думает за меня». Она – «тьютор». Она не даст мне принять «заведомо неправильное решение». Или это решение будет как минимум осознанным.
Всеволод: Можно ли измерить качество рефакторинга? Отсутствие необходимости повторного рефакторинга?
Александр: Боюсь, что это не критерий. Хотя если конечно удалось добиться отсутствия подобной необходимости, то рефакторинг, наверное, идеальный. Другое дело, что я не видел кода/архитектуры, которая была бы идеальна. Если что-то идеально «сверху», то оно неидеально «снизу». Или наоборот, «снизу» идеальный код, а «сверху» – неидеальные архитектурные решения.
Всеволод: Мы очень часто говорим о рефакторинге, а многие это воспринимают негативно. Например, «все работало, но потом мы сделали рефакторинг». Можно ли сказать, что при использовании UML-моделирования коды изначально стали луче, а рефакторинг стал реже?
Максим: Если всё работает, и никаких внешних изменений не происходит, то не нужно делать рефакторинг. Рефакторинг это не самооцель, это способ достигать цели более быстро и точно, не давать коду превратиться в «салат оливье». Использование UML с кодогенерацией даже в самом простом варианте сразу автоматически структурирует код. Так что, конечно, «да».
Всеволод: Болезненный вопрос – синхронизация UML-диаграмм и программного кода. Достигается ли 100% соответствие?
Александр: Достигается. За счёт того, что есть 100% кодогенерация из модели в код. Пока её не было, я сам был большим противником моделирования. Я считал рисование диаграмм «мартышкиным трудом».
Максим: Да, за счет того, что нет реверса, синхронизация всегда в одну сторону. И мимо нее вы ни как не пройдете.
Всеволод: Т.е. кодогенерация – неотъемлемое условие эффективного внедрения UML в процесс разработки?
Александр: Безусловно. Более того, если нет кодогенерации, причём постоянной, то в какой-то момент диаграмма начинает быть «просто картинкой». Она «протухает». Она «ни о чём».
Ну и конечно, как мы уже говорили, кодогенерация позволяет «кодировать» код, извиняюсь за тавтологию, – сильно быстрее. Поверьте мне, выше примеры уже приводились, и их еще миллион у меня.
Максим: Я бы сказал категоричнее, кодогенерация – это ключевой смысл использования UML. По крайней мере, для нас. Понимаете, в чем штука, я сейчас скажу странную вещь, идущую в разрез с темой нашей дискуссии: мы вообще не используем UML в классическом смысле.
Всеволод: ну вот, «приехали», поясните, что Вы имеете в виду?
Максим: Не пугайтесь, я конечно немного утрирую. Как я уже намекал выше, UML для нас это лишь форма «квадратиков», конкретная графическая нотация и не более того. Мы взяли ее как набор примитивов в графическом редакторе, и построили из них что-то существенно большее. Шаблоны, о которых мы говорили выше, позволяют нам описать любую метамодель в терминах этих UML-примитивов, Александр выше их уже перечислял, «Класс», «Категория», «Связь» и т.д. И в результате получить новые термины, новые примитивы, уже уровнем выше и притом рекурсивно, если надо, используя которые, конкретный проектировщик или программист и будет создавать конкретную модель и получать из нее код. Т.е. фактически, шаблоны – это то, что формирует конкретный DSL и компилирует модель, на нем нарисованную, в код, а UML это просто способ рисования этой модели. Т.е. человек, даже хорошо знакомый с классическим UML, может далеко не сразу «въехать» в то, что увидит нарисованным у нас.
Всеволод: Оправдываются ли «потери времени» на создание кода в графической нотации повышением его качества?
Александр: С моей точки зрения – оправдываются. За счёт возможности стереотипов более высокого уровня. Как только видим, что проектные решения начинают повторяться. Более того модель влияет на фреймворк разработки, а фреймворк – влияет на модель. Ну и рефакторинг при наличии модели (и тестов) переходит на совершенно иной по качеству уровень, как выше уже говорилось.
Максим: Александр сказал о стереотипах высокого уровня, это отдельная очень важная тема, и это самый мощный механизм который сейчас нам известен. Это как раз то, что позволяет превращать атомы в моллекулы, моллекулы в сплавы, детали, агрегаты и, наконец, в готовый продукт.
Всеволод: Вы упомянули тесты в контексте рефакторинга. Значит ли это, что в целом без тестов нечего думать о рефакторинге, а без рефакторинга и моделирование не особо нужно?
Александр: На мой взгляд, лучше, конечно, сначала тесты, а потом UML, моделирование, кодогенерация. Но у нас это исторически происходило наоборот. Но я бы теперь конечно предпочёл бы сначала тесты. Мы не «просто думаем о тестах». Мы знаем, что они есть. Что они «завтра покажут ошибки». И показывают. Но и конечно – «мы думаем о тестах». Мы пишем «упреждающие» тесты. У нас есть «регрессионые тесты». Есть тесты, которые тестируют требования. И они позволяют рефакторить без сильного ущерба качеству.
Максим: не соглашусь с Александром, мне кажется, что это взаимодополняющие вещи, и с чего начать, не так важно. Скорее, это должно определяться внутренней культурой и предпочтениями коллектива. Я бы вот все равно с моделирования начал, в конце концов, тесты тоже можно и нужно генерировать, а не писать вручную.
Всеволод: Александр, как я понял, изначально Вы были противником UML? Оглядываясь назад (возможно, с улыбкой), можно ли сказать, что Вы думали так: «зачем мне UML, я и так контролирую свой код?»
Александр: Как раз у меня лично давным-давно было осознание того, что «проблемы есть». Но без кодогенерации и постоянной синхронизации с моделью я не понимал, как UML может мне лично помочь. Теперь – понимаю и пользуюсь каждый день, хотя это конечно уже непросто UML, как мы выше рассказали.
Всеволод: Стал ли «ваш UML» еще одним способом документировать программный код?
Александр: Да конечно. Более того у нас все модели интегрируются в общую гипертекстовую базу знаний. В ней содержится также bug-tracking и требования.
Максим:… и еще много чего, что есть наше know-how. Кстати, интеграция тоже построена на тех же самых шаблонах: html для базы генерируется из модели, как и исходный код, и автоматически помещается на сервер.
Всеволод: В каком виде UML-модели интегрируются? Не в виде же картинок!
Александр: В виде картинок с гиперссылками и поясняющей документацией. Но от картинки всегда можно перейти к реальной диаграмме в инструменте работы с UML.
Максим: и наоборот, а еще к ним же автоматом привязываются изменяющие задачи и коммиты в репозитарий.
Всеволод: Используются ли UML-диаграммы для взаимодействия с «не-программистами»?
Максим: В начале внедрения мы пытались использовать UML для взаимодействия. Все требования были спроектированы в виде диаграмм UseCase и Sequence. В максимально понятном, как нам казалось, для не специалиста виде. Первую версию системы Ф1, наши коллеги юристы и маркетологи, ставящие задачу, честно попытались прочитать с диаграмм, и у них это даже получилось. Но…, скажем так, практика не прижилась.
Александр: Все-таки, не технари (не по образованию, а по призванию), не очень любят всякие формальные языки. Им подсознательно проще на русском литературном, а не на UML. Это уже из разряда психологии.
Всеволод: Какие UML-диаграммы и в каком объеме используются?
Александр: Прежде всего, диаграммы классов и UseCase’ов.
Всеволод: Вы находите другие диаграммы менее полезными? Например, «мозгов» программистов хватает, чтобы обойтись без диаграммы «объектов»?
Максим: Нет. Иногда используем диаграммы состояний, описываем на них и генерируем код state machines. Но это очень редко. Использование конечных автоматов, на формальном уровне, тоже требует определенной заточки мозга. Еще кое-где используются sequence, но исключительно как иллюстрация, т.е. как «мертвая диаграмма», сейчас из них ни чего не генерируется, а значит, как выше говорил Александр, время жизни у нее очень короткое.
Александр: Возможно, Sequence даже более полезны, просто пока не дошли руки сделать для них соответствующую кодогенерацию. А диаграмма «объектов» это как раз те самые цепочки SAX-фильтров. Когда оперируем уже не классами, а их экземплярами.
Всеволод: Классический RAD-подход зажимает программиста в достаточно узкие рамки. База данных, интерфейс, форма, компоненты, события, процедуры отклика. Насколько удалось расширить эти рамки за счёт UML?
Александр: UML «зажимает» в рамках той «мета-модели», которая разработана. Но к этому отчасти и стремились. По сравнению с RAD – гораздо активнее используются примеси и элементы АОП. А также декларативный, а не императивный подход к некоторым частям проектов. Т.е. на модели рисуется «модель данных» по максимуму оторванная от того во что она физически должна превратиться, а в «активный код» она трансформируется уже при помощи кодогенератора. Например «схема документа» или набор «настроек» проекта. Разного рода цепочки SAX-фильтров, и т.д.
Максим: Важно, что он зажимает там, где вы сами решите. Метамадель не навязывает никаких правил своему автору, она навязывает их только модели и тому, кто ее будет создавать. В одном месте вы зажимаете, а в другом – даете уникальную гибкость и скорость lego-конструктора.
Всеволод: Как использование моделей повлияло на архитектуру приложения и структуру программного кода?
Александр: Дело конечно не только в моделях. Дело в «гигиене мозга». Но правильное использование моделирования повышает её. И, да, модель – помощник. Оговорюсь, если нет желания улучшать, то никакие модели не помогут. Как, впрочем, и любые другие практики. А на архитектуру UML и моделирование влияет положительно 🙂 Появляются логические слои. Исчезают паразитные циклические зависимости, и тд.
Всеволод: Поработаем в режиме «блица». Когда, по-вашему, не нужно применять UML и моделирование?
Александр: Для НИР и прочих исследований. Для проекта «на два-три месяца», который потом, скорее всего, не придётся поддерживать.
Максим: Если UML используется только для рисования, то согласен с Александром, но если для метамоделирования и кодогенерации, то я считаю что использовать это можно и нужно всегда, особено если вы привыкли это делать. Разумеется если это не «hello world». Хотя мне, наверное, и «hello world» будет проще сгенерировать, по крайне мере не нужно вспоминать как делать новый проект в IDE :), ведь проекты у нас тоже генерируются.
Всеволод: В какой момент развития и по каким признакам можно сказать, что тот способ которым вы используете UML эффективен?
Александр: Достаточно сравнить время написания какой-нибудь функциональности «вручную» или вашим способом.
Максим: Единственное, существуют еще технические ограничения конкретного инструмента, иногда они могут оказывать крайне негативное влияние.
Всеволод: Как поэтапно приступить к внедрению моделирования в производственный процесс?
Александр: Можно начать в любой момент. Главное, сразу не пытаться «объять необъятное».
Всеволод: Какую часть задач можно тесно интегрировать с UML и генерацией кода, а что можно оставить «как есть»?
Максим: оставлять можно какие-то не сильно структурированные, уникальные алгоритмы, что-то, в чем нет шаблона, повторного использования, какой-то системы, которую можно формализовать и репродуцировать.
Александр: У меня самого далеко не всё переведено на модель. Я в первую очередь перевожу либо новые классы/сущности. Или те, которые затрагиваю в процессе переделок. Либо те, которые «рефакторю».
Всеволод: Наверное, были испробованы и другие подходы. Есть ли альтернатива UML в современном мире разработки ПО?
Максим: Для нас, в том виде, в котором мы его используем, — нет. Но я выше уже говорил, что это вообще не очень принципиально, каким конкретным образом рисовать стрелочки с квадратиками. Главное, что мы с ним делаем, а не как это выглядит.
Александр: Уточню, на самом деле альтернатив именно UML много, и именно потому, что для нас это просто способ описания модели. Можно взять любую другую графическую нотацию, которая имеет в базисе необходимые нам понятия, и использовать ее. Можно описать свою. Можно вообще, и я об этом активно думаю, перейти на текстовое описание модели, т.е. создать еще один DSL. Вариантов много и их можно комбинировать.
Всеволод: Есть ли некие пределы возможностей вашего подхода?
Максим: как только вы начали создавать собственные мета-модели, любые ограничения исчезают, может не хватать лишь фантазии и умения видеть в частном общее.
Всеволод: Неужели все так замечательно, и нет прямо ни каких проблем? Хочется в лучших театральных традициях закричать: «Не верю»!
Александр: Конечно, есть. Прежде всего, ограничения инструмента. Недостаточная скорость генерации на больших моделях. Неоптимальность интерфейса UML редактора и т.д.
Максим: проблема не идеологическая, а технологическая. Мы постепенно вырастаем из инструментария, который был сделан достаточно давно и долгие годы сохранял свою актуальность. Генератор требует обновления, внесения модификаций. Но это скорее не «проблема», а «задача». Мы используем в качестве UML-чертилки тоже отдельный пакет. Да, он уже часть нашего «конвейера», но и его не мешало бы улучшить, обновить.
Всеволод: Ваши планы?
Александр: Написать полностью свой инструмент — редактор модели. Так сказать, сделать «работу над ошибками» в уже существующих.
Максим: И при том – кроссплатформеннный. В общем, в этом направлении тоже есть куча идей, как сделать процесс моделирования еще более эффективным.
Всеволод: В следующей статье обсудим DSL?
Максим: Да, конечно.
Александр: С удовольствием!
Всеволод: Как сотрудник Embarcadero, пользуясь служебным положением, спрашиваю… Вы используете Delphi?
Александр: Да, я применяю Delphi в разработке. Я знаю о противоречиях между RAD и UML, и я стараюсь использовать сильные стороны каждой из технологий и достигаю значительной синергии. Я не использую Delphi как чистый RAD-инструмент для размещения кнопок на формах. Это – одна из компонент достаточно сложной технологической цепочки. Сейчас мы в процессе перехода на Delphi XE4. Есть потребность в 64-битах.
Максим: Я вырос на С++, но в сильно гетерогенной среде, в проектах, в которых я участвовал, в одно и тоже время применялись Delphi, С++, python, java и тд, поэтому я ни когда не был сторонником «холиваров» что «лучше» — каждой одежке по застежке. Как руководителю проекта для меня главное — эффективность решения в целом. В нашей компании используют Delphi очень давно и интенсивно, главное знать сильные и слабые стороны каждого инструмента, его потенциал, правильно принимать на основе этого решения, выстраивать технологический цикл, подбирать профессионалов так, что бы они максимально соответствовали инструменту. В этом смысле, Delphi нам вполне подходит.