Статьи

Подход к диаграммам UML и моделям ER, приемлемым для инженера-программиста

В рамках моей нынешней работы в Groupon мне приходится создавать диаграммы, те красивые картинки, которые радуют руководителей проектов. Я пишу базовые UML-диаграммы (диаграммы состояний и диаграммы деятельности) вместе с  диаграммами Entity-Relationship  (да, для БД).

FOyn3i8m34Ltd-AhUo_G0Q5I2R4XLHMpavesearAueAuFGa3a_Nz_9_aOrAEkgyBaJfT1DKrPsVTnbuJQlJotCLRGUTuYhnMH6mrH0n98fcm-v7Z1zLD3Cx3fGAdCcbaPSCfzrgYSelwK00Qd1Pd7mWUQJUhKwAophHhCqJFBu7EWcB07_uK3Vevl663lxkuiheisNWIegFuCN_n1G00

687474703a2f2f6275726e7473757368692e6e65742f73747566662f6572642d6578616d706c652d73696d706c652e706e67

Да, люди хотят эти картинки, и я должен их создавать

Что не так с предыдущим процессом

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

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

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

Мой текущий процесс

Я предпочитаю текстовые форматы вместо хороших редакторов WYSIWYG, потому что:

  • текст переносим, ​​в то время как каждый редактор WYSIWYG, как правило, имеет свой собственный формат
  • Вы можете легко сравнивать и объединять текстовые файлы

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

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

В настоящее время я использую  PlantUML  для UML диаграмм и  ERD  для ER диаграмм. Эрд выигрывает дополнительные очки, потому что написано на Хаскеле. Есть также хороший сайт, который предлагает веб-редактор для PlantUML: он называется  PlantText .

Теперь у этого решения есть проблемы:

  • в любом случае вам нужно установить программное обеспечение, по крайней мере, для диаграмм ER (вы можете сгенерировать диаграммы UML с помощью веб-сайта planttext).
  • нет хороших редакторов, поддерживающих DSL, используемые для описания этих диаграмм
  • нет никакой интеграции между веб-редактором и моим репозиторием GitHub
  • Вам необходимо обновить изображения в документах после их создания

Идеальный процесс

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

  1. Мне не пришлось бы обновлять все документы, содержащие диаграмму, при изменении диаграммы. Проблема в том, что многие документы имеют копию изображения, а не ссылку. Другая проблема заключается в том, что сервер с диаграммами должен быть всегда включен.
  2. Я бы знал, где найти источник диаграммы. Например, я представляю, что у нас может быть изображение, например, на  http://diagrams.foo.com/diagram1.png,  и веб-приложение для его редактирования на http://diagrams.foo.com/diagram1.png / редактировать .

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

Что я начал делать: подсветка синтаксиса для PlantUML

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

Я реализовал подсветку синтаксиса для большей части PlantUML для веб-редактора CodeMirror. Код доступен на GitHub  и я послал  запрос тянуть  на  PlantUML-сервер .

Написание режима подсветки синтаксиса может напоминать написание грамматики, на самом деле моей первой мыслью было написание грамматики для ANTLR, а затем реализация автоматического преобразования из EBNF в режим CodeMirror. Однако цель грамматики и систем выделения синтаксиса различна. Первый предназначен для анализа  правильных  файлов и остановки при обнаружении ошибок (только очень хорошие грамматики хорошо справляются с ошибками и способны преодолеть несколько ошибок), в то время как система подсветки синтаксиса работает с документом, который  все  время неверен : поскольку вы печатаете документ неверно, только после завершения выписки документ будет корректным, пока вы не начнете вводить следующий символ, и документ снова будет неправильным. Система подсветки синтаксиса должна быть очень надежной и допускать множество ошибок.

Это случайная часть режима, который я определил.

            } else if (state.name === "stereotype"){                
                if (stream.match(/\(/)) {
                    state.name = "stereotype style"
                    return null;
                }
                if (stream.match(/[A-Za-z][A-Za-z_0-9]*/)) {
                    return "variable";
                }
                if (stream.match(/>>/)) {
                    state.name = state.old_state;
                    return null;
                }
                if (stream.match(/,/)) { return null; }             
            } else if (state.name === "stereotype style"){
                if (stream.match(/\)/)) {
                    state.name = "stereotype"
                    return null;
                }
                if (stream.match(/[A-Z]/)) {
                    return "string";
                }   
                if (matchColors(stream, state)){
                    return "atom";
                }
                if (stream.match(/[,]+/)) {
                    return null;
                }      
            } else if (state.name === "class def"){
                if (stream.match(/[\t ]+/)) {
                    return null;
                }               
                if (stream.match(/\}/)) {
                    state.name = "base";
                    return "bracket";
                }
                if (stream.match(/\.\./)) { 
                    state.name = "class def section";
                    return "operator"; 
                }                                                                                    
                if (stream.match(/==/)) { 
                    state.name = "class def section";
                    return "operator"; 
                }      
                if (stream.match(/--/)) { 
                    state.name = "class def section";
                    return "operator"; 
                }
                if (stream.match(/__/)) { 
                    state.name = "class def section";
                    return "operator"; 
                }
                if (stream.match(/\+|-|#|~/)) {
                    if (isMethod(stream)) {                  
                       state.name = "class def method";
                    } else {
                        if (hasTypeAfter(stream)) {
                            state.name = "class def attribute (type after)";
                        } else {
                            state.name = "class def attribute";
                        }
                    }
                    return "attribute";
                }

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

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

MT("static class methods",
    "[keyword class] [def Car] [bracket {]",
    "  [operator ..][string Method Examples ][operator ..]",
    "  [attribute +][keyword {static}] [def Name][operator ():] [variable Type] [bracket {] [variable arg1], [variable arg2], [variable argn] [bracket }]"
  );     

  MT("abstract class methods",
    "[keyword class] [def Car] [bracket {]",
    "  [operator ..][string Method Examples ][operator ..]",
    "  [attribute +][keyword {abstract}] [def Name][operator ():] [variable Type] [bracket {] [variable arg1], [variable arg2], [variable argn] [bracket }]"
  );

  MT("interfaces examples",
    "[keyword class] [def Car]",
    "[variable ICar] [operator ()-] [variable Car]",
    "[variable ICar2] [operator ()--] [variable Car]",
    "[variable Car] [operator -()] [variable ICar3]"
  );

  MT("node package",
    "[keyword package] [def Node] <<[builtin Node]>> [bracket {]",
    "    [keyword class] [def Worker1]",
    "[bracket }]"
  );

Рассмотрим первый тест: он говорит, что первое слово ( класс ) должно распознаваться как ключевое слово, а второе ( автомобиль ) — как определение (или определение).

Единственная проблема при написании этого кода состоит в том, что грамматика plantuml… неоптимальна. Он используется для множества различных типов диаграмм, и он мне не так понятен. Я определенно не предложил бы это как пример хорошо разработанного DSL.

Что я хочу сделать в будущем

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

Следующим шагом является написание веб-приложения вокруг программы erd. Я начал создавать проект ( erd-web-server ), посмотрим, когда я найду время, чтобы немного поиграть с Haskell…

Как только я это сделаю, я начну работать над GitHub. Я хотел бы получить доступ к диаграммам в моих проектах и ​​генерировать изображения как часть веб-хука git.

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