12. Unified Modeling Language

12.1. UML Introduction

UML (Unified Modeling Language) is an ISO standardized method for describing programs. Its resulting diagrams are widely taught and should therefore be used as much as possible. The standard is mostly about the inner mechanism of the modeling language and not so much about the diagrams, however, the diagrams are the most useful part, as they are quickly understood even by non-programmers.

12.1.1. Getting Started with UML Diagrams

Check out these web-sites

This is the inheritance hierarchy of UML diagrams supported by PlantUML (clickable in HTML):

' -*- plantuml -*-
' Copyright (C) 2018, Wolfgang Scherer, <Wolfgang.Scherer at gmx.de>
'
' This file is part of Documentation Standard.
'
' This program is free software; you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation; either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful, but
' WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
' General Public License for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program. If not, see <http://www.gnu.org/licenses/>.

@startuml
/' |:here:| '/

skinparam padding 1

left to right direction

hide members

class "UML 2.5 Diagram" as U2 [[https://www.uml-diagrams.org/uml-25-diagrams.html]] {
}
class "Structure Diagram" as RD [[https://www.uml-diagrams.org/uml-25-diagrams.html#structure-diagram]] {
}
class "Class Diagram" as CD <<(c, lightblue)>> [[https://www.uml-diagrams.org/class-diagrams-overview.html]] {
}
class "Object Diagram" as OD <<(o, lightblue)>> [[https://www.uml-diagrams.org/class-diagrams-overview.html#object-diagram]] {
}
class "Component Diagram" as PD <<(p, lightblue)>> [[https://www.uml-diagrams.org/component-diagrams.html]] {
}
class "Deployment Diagram" as DD <<(d, lightblue)>> [[https://www.uml-diagrams.org/deployment-diagrams-overview.html]] {
}
class "Behavior Diagram" as BD [[https://www.uml-diagrams.org/uml-25-diagrams.html#behavior-diagram]] {
}
class "Use Case Diagram" as UC <<(u, goldenrod)>> [[https://www.uml-diagrams.org/use-case-diagrams.html]] {
}
class "Activity Diagram" as AD <<(a, goldenrod)>> [[https://www.uml-diagrams.org/activity-diagrams.html]] {
}
class "State Machine Diagram" as SM <<(s, goldenrod)>> [[https://www.uml-diagrams.org/state-machine-diagrams.html]] {
}
class "Interaction diagram" as ID [[https://www.uml-diagrams.org/uml-25-diagrams.html#interaction-diagram]] {
}
class "Message Sequence Diagram" as MS <<(m, goldenrod)>> [[https://www.uml-diagrams.org/sequence-diagrams.html]] {
}
class "Timing Diagram" as TD <<(t, goldenrod)>> [[https://www.uml-diagrams.org/timing-diagrams.html]] {
}

hide U2 circle
hide RD circle
hide BD circle
hide ID circle

U2 <|-- RD
RD <|-- CD
RD <|-- OD
RD <|-- PD
RD <|-- DD
U2 <|-- BD
BD <|-- UC
BD <|-- AD
BD <|-- SM
BD <|-- ID
ID <|-- MS
ID <|-- TD

' (progn (forward-line 1) (snip-insert-mode "puml.t.ide" t) (insert "\n"))
' :ide-menu: Emacs IDE Menu - Buffer @BUFFER@
' . M-x `eIDE-menu' ()(eIDE-menu "z")

' :ide: OCCUR-OUTLINE: Sections: `||: sec :||'
' . (x-symbol-tag-occur-outline "sec" '("||:" ":||") '("|:" ":|"))

' :ide: MENU-OUTLINE:  Sections `||: sec :||'
' . (x-eIDE-menu-outline "sec" '("||:" ":||") '("|:" ":|"))

' :ide: OCCUR-OUTLINE: Default `|||: sec :|||'
' . (x-symbol-tag-occur-outline)

' :ide: MENU-OUTLINE:  Default `|||: sec :|||'
' . (x-eIDE-menu-outline)

' :ide: +-#+
' . Buffer Outline Sections ()

' :ide: PLANTUML: HELP
' . (let ((args (concat (file-name-sans-extension (buffer-file-name)) ""))) (compile (concat "plantuml -h ")))

' :ide: PLANTUML: this file's EPS
' . (let ((args (concat (file-name-sans-extension (buffer-file-name)) ".eps"))) (compile (concat "plantuml -teps " (buffer-file-name))))

' :ide: PLANTUML: this file's PDF
' . (let ((args (concat (file-name-sans-extension (buffer-file-name)) ".pdf"))) (compile (concat "plantuml -tpdf " (buffer-file-name))))

' :ide: PLANTUML: this file's PNG
' . (let ((args (concat (file-name-sans-extension (buffer-file-name)) ".png"))) (compile (concat "plantuml -tpng " (buffer-file-name))))

' :ide: PLANTUML: this file's SVG
' . (let ((args (concat (file-name-sans-extension (buffer-file-name)) ".svg"))) (compile (concat "plantuml -tsvg " (buffer-file-name))))

' :ide: PLANTUML: this file's PNG + display
' . (let ((args (concat (file-name-sans-extension (buffer-file-name)) ".png"))) (compile (concat "plantuml -tpng " (buffer-file-name) " && display " args)))

' :ide: PLANTUML: this file's PNG + VIEW
' . (let ((args (concat (file-name-sans-extension (buffer-file-name)) ".png"))) (shell-command (concat "plantuml -tpng " (buffer-file-name) " ")) (view-file-other-window args))

' :ide: PLANTUML: this file's SVG + VIEW
' . (let ((args (concat (file-name-sans-extension (buffer-file-name)) ".svg"))) (shell-command (concat "plantuml -tsvg " (buffer-file-name) " ")) (view-file-other-window args))

' Local Variables:
' mode: plantuml
' mode: snip-minor
' snip-show-inactive: t
' snip-mode: plantuml
' truncate-lines: t
' comment-start: "'"
' comment-end: ""
' End:
@enduml

12.1.4. UML Tools

The site UML tools : Curated selection of free, online, OSS, for MAC,… tools is an extensive collection of UML tool information.

12.1.5. UML GUI Tools

Many UML tools specialize in providing a GUI environment to design diagrams with extensive support for a detailed standards-compliant specification of elements. The output is standardized but unreadable XMI (XML Metadata Interchange). This disqualifies most of the UML GUI tools for proper adhoc ASCII level documentation.

Creating unreadable diagram defintions, which require HTML/PDF/image generation before they become useful is highly undesirable if your focus is on programming. The incentive for a programmer to keep the documentation up to date is negative[1].

GUI Tools are still useful to explore the graphical notation of the various diagrams. See ref:gui tools with uml standard support.

12.1.6. Tools for Translating Declarative Specs to Diagrams

If your focus is on modeling, the UML GUI Tools are a better choice, but if your main task is programming, this is the way to go. The introduction to UMLGraph provides an excellent argument for declarative specification of UML diagrams.

The Stack Overflow question “Generating UML diagrams from textual representation” references a list of tools (Text to UML tools), but highlights PlantUML.

The PlantUML tool’s definition language is quite readable without the rendered diagrams. It also integrates with many tools (namely Emacs and Sphinx)- (See section Sequence Diagram). The GitHubGist generate PlantUML definition from python sources provides genclass.py, locally renamed to gen_plantuml.py. This command helps with the initial UML class diagrams and further updates. See section Auto generators for an example generator of a class diagram for global variables/functions.

yUML has sphinx integration, but is very limited in regarding readability and choice of diagrams (see section yUML).

Umple: Merging Modeling with Programming, i.e. it works as a programming language pre-processor (programming language extension, roundtrip).

12.2. UML Diagrams

The most important UML Diagrams are

12.2.1. Use Case Diagram

Use case diagrams are usually referred to as behavior diagrams used to describe a set of actions (use cases) that some system or systems (subject) should or can perform in collaboration with one or more external users of the system (actors). Each use case should provide some observable and valuable result to the actors or other stakeholders of the system. (https://www.uml-diagrams.org/use-case-diagrams.html)

Here is the use case diagram of a workers day:

.. uml::

   @startuml
   worker1   -> (work)
   worker2   -> (work)
   worker1   -> (home1)
   worker2   -> (home2)
   @enduml

This is how it is rendered in Sphinx:

@startuml
worker1   -> (work)
worker2   -> (work)
worker1   -> (home1)
worker2   -> (home2)
@enduml

For details see PlantUML Language Reference Guide.

12.2.2. Class Diagram

Class diagram is UML structure diagram which shows structure of the designed system at the level of classes and interfaces, shows their features, constraints and relationships - associations, generalizations, dependencies, etc. (https://www.uml-diagrams.org/class-diagrams-overview.html)

PlantUML example (PlantUML Language Reference Guide):

.. uml::

   @startuml
   class OneClass {
     -private : string
     +public : int
     -priv_method()
     +pub_method()
   }
   ' stereotype
   class TwoClass  << (I, #ffcccc) interface >>
   OneClass ..|> TwoClass
   hide TwoClass members
   @enduml

This is how it is rendered in Sphinx:

@startuml
class OneClass {
  -private : string
  +public : int
  -priv_method()
  +pub_method()
}
' stereotype
class TwoClass  << (I, #ffcccc) interface >>
OneClass ..|> TwoClass
hide TwoClass members
@enduml

12.2.2.1. Associations between classes

UML association is relationship between classifiers to show that instances of classifiers could be either linked to each other or combined into some aggregation. See the definitive Guide to UML associations.
Association:

is a relationship where all objects have their own lifecycle and there is no owner.

Let’s take an example of Teacher and Student. Multiple students can associate with single teacher and single student can associate with multiple teachers, but there is no ownership between the objects and both have their own lifecycle. Both can be created and deleted independently.

Aggregation:

is a specialised form of Association where all objects have their own lifecycle, but there is ownership and child objects can not belong to another parent object.

Let’s take an example of Department and teacher. A single teacher can not belong to multiple departments, but if we delete the department, the teacher object will not be destroyed. We can think about it as a “has-a” relationship.

Composition:

is again specialised form of Aggregation and we can call this as a “death” relationship. It is a strong type of Aggregation. Child object does not have its lifecycle and if parent object is deleted, all child objects will also be deleted.

Let’s take again an example of relationship between House and Rooms. House can contain multiple rooms - there is no independent life of room and any room can not belong to two different houses. If we delete the house - room will automatically be deleted.

Let’s take another example relationship between Questions and Options. Single questions can have multiple options and option can not belong to multiple questions. If we delete the questions, options will automatically be deleted.

Source: StackExchange

PlantUML offers various association types:

Relation Symbol Description
Association -- no owner, navigability unspecified
Directed Association <-- no owner, navigable
Association x-- no owner, not navigable
Aggregation o-- owner, but independent
Composition *-- owner and dependent
Dependency <.. no owner
Generalization/Extension <│--  
Realization <│..  
Nesting +--  

It is possible to replace -- by .. for broken lines.

12.2.2.2. Class Diagram Examples

Naming conventions, visibility and types in global namespace and within classes:

@startuml
class "global" as G0 << (G, #ffcccc) >> {
  VARIABLE_NAME
  ClassName
  function_name()
}
note bottom: Naming conventions\nin global namespace

class "Class" as C0 {
  {field} field_name_1
  {field} field_name_2
  {method} method_name_1()
  {method} method_name_2()
}
note right: Naming conventions\nin class

class "ClassWithStuff" as C1 {
  -_private
  +attrib
  +list[]
  +dict{}
}
C1 : -_private_method()
C1 : +method()
note right: Visibility and type

G0 -right[hidden]- C0
C0 -down[hidden]- C1
@enduml

Enums can be used to describe variable collections:

@startuml
enum SOME_STUFF_ {
  SOME_STUFF_HERE
  SOME_STUFF_THERE
  SOME_STUFF_ANY
  SOME_STUFF_WHERE
}
hide SOME_STUFF_ methods
@enduml

Here is a more elaborate class diagram in _static/big-brother.puml:

@startuml
!include ws-cartoon-logo.puml
class "<$ws> BigBrother" as BB {
    who[]
    who_not[]
    --
    watch_em()    : who
    check_em()    : who_not
    ..
    check(p)      : suspect/vindicate
    watch(p,time) : boring/strange
    suspect(p)    : move //p// from //who_not// to //who//
    vindicate(p)  : move //p// from //who// to //who_not//
}
hide BB circle
@enduml

It can be included with the uml directive:

.. uml:: _static/big-brother.puml
   :caption: Big Brother is watching you

@startuml
!include ws-cartoon-logo.puml
class "<$ws> BigBrother" as BB {
    who[]
    who_not[]
    --
    watch_em()    : who
    check_em()    : who_not
    ..
    check(p)      : suspect/vindicate
    watch(p,time) : boring/strange
    suspect(p)    : move //p// from //who_not// to //who//
    vindicate(p)  : move //p// from //who// to //who_not//
}
hide BB circle
@enduml

figure 12.1 Big Brother is watching you

or specify path to external PlantUML file:

@startuml
Foo <|-- Bar
@enduml

You can specify height, width, scale and align:

Foo <|-- Bar

You can specify a caption:

Foo <|-- Bar

figure 12.2 Caption with bold and italic

|:todo:| describe uml directive explicitely

If the uml directive has a caption (via option :caption:), it behaves like the figure directive. Otherwise, it behaves like the image directive.

12.2.3. Activity Diagram

Activity diagram is UML behavior diagram which shows flow of control or object flow with emphasis on the sequence and conditions of the flow. The actions coordinated by activity models can be initiated because other actions finish executing, because objects and data become available, or because some events external to the flow occur. (https://www.uml-diagrams.org/activity-diagrams.html)

Note

It is not a data flow diagram!

See State Machine Diagram vs Activity Diagram for a basic explanation of the differences.

Here is the activity diagram of a persons daily routine:

.. uml::

   @startuml
   !include _static/call-bevahor.puml
   start
   :alarm clock rings;
   :turn off the alarm clock;
   if(is it weekend?) then (yes)
     :go back to sleep;
   else (no)
     :eat;
     :wash;
     :CALL(go to work);
   endif
   stop
   @enduml

This is how it is rendered in Sphinx:

@startuml
!include _static/call-behavior.puml
start
:alarm clock rings;
:turn of the alarm clock;
if(is it weekend?) then (yes)
  :go back to sleep;
else (no)
  :eat;
  :wash;
  :CALL(go to work);
endif
stop
@enduml

For details see PlantUML Language Reference Guide.

12.2.3.1. Activity Diagram Examples

Here is the activity diagram to check a person BB.check(person):

start
:assume person is boring;
while (person is boring\n**and** time is not up) is (yes)
  if (person acts strange) then (yes)
    :person is now suspicious;
  else (no)
  endif
endwhile (no)
if (is person suspicious?) then (yes)
  :BB.suspect(person);
else (no)
  :BB.vindicate(person);
endif
stop

Another activity Diagram:

@startuml

start
  if (Graphviz installed ?) then (yes)
    :process all\ndiagrams <$ws>;
  else (no)
    :process only
    __sequence__ and __activity__ diagrams;
  endif
stop
@enduml

figure 12.3 Activitiy of the day

12.2.4. State Machine Diagram

State machine diagram is a behavior diagram which shows discrete behavior of a part of designed system through finite state transitions. State machine diagrams can also be used to express the usage protocol of part of a system. (https://www.uml-diagrams.org/state-machine-diagrams.html)

Here is the State Machine Diagram of the procedure of starting a software project:

.. uml::

   @startuml

   state   plan   as "Planning Project"
   plan :            identify necessary UML diagram

   state   create as "Create UML Diagram"

   state   draw   as "Draw UML Diagram"
   draw :             modify diagram

   state   bless  as "Blessing"
   bless :           discuss UML Diagram with boss

   state   coding as "Programming"

   state   doc    as "Document"

   [*] --> plan
   plan   --> bless   : finished
   create --> draw
   draw   --> bless

   bless  --> plan    : missing diagram
   bless  --> plan    : superfluous diagram
   bless  --> create  : new diagram\nblessed
   bless  --> draw    : error in\ndiagram
   bless  --> coding  : all diagrams\nfinished

   coding --> doc     : new object
   doc    --> coding  : finished
   doc    --> plan    : missing diagram
   coding --> [*]
   @enduml

This is how it is rendered in Sphinx:

@startuml

state   plan   as "Planning Project"
plan :            identify necessary UML diagram

state   create as "Create UML Diagram"

state   draw   as "Draw UML Diagram"
draw :             modify diagram

state   bless  as "Blessing"
bless :           discuss UML Diagram with boss

state   coding as "Programming"

state   doc    as "Document"

[*] --> plan
plan   --> bless   : finished
create --> draw
draw   --> bless

bless  --> plan    : missing diagram
bless  --> plan    : superfluous diagram
bless  --> create  : new diagram\nblessed
bless  --> draw    : error in\ndiagram
bless  --> coding  : all diagrams\nfinished

coding --> doc     : new object
doc    --> coding  : finished
doc    --> plan    : missing diagram
coding --> [*]
@enduml

See Choice Pseudostate and Guard Condition in State Diagrams for additional state diagram syntax:

@startuml
left to right direction

state choice <<choice>>
state alt
state fork <<fork>>
state p1
state p2
state join <<join>>
state end <<end>>

[*] --> choice
choice --> alt
alt --> fork

choice --> fork
fork --> p1
fork --> p2
p1 --> join
p2 --> join

join --> end
@enduml

rendered as:

@startuml
left to right direction

state choice <<choice>>
state alt
state fork <<fork>>
state p1
state p2
state join <<join>>
state end <<end>>

[*] --> choice
choice --> alt
alt --> fork

choice --> fork
fork --> p1
fork --> p2
p1 --> join
p2 --> join

join --> end
@enduml

12.2.5. Sequence Diagram

Sequence diagram is the most common kind of interaction diagram, which focuses on the message interchange between a number of lifelines.

Sequence diagram describes an interaction by focusing on the sequence of messages that are exchanged, along with their corresponding occurrence specifications on the lifelines. (https://www.uml-diagrams.org/sequence-diagrams.html)

Here is the sequence diagram of a short conversation between me and my boss:

.. uml::

   Dominik  -> Wolfgang : Am I doing\nit correctly?
   Wolfgang -> Dominik  : Nice try\ndo it again!

This is how it is rendered in Sphinx:

Dominik -> Wolfgang: Am I doing\nit correctly?
Wolfgang -> Dominik: Nice try\ndo it again!

For details see PlantUML Language Reference Guide.

12.2.5.1. Sequence Diagram Examples

Alice -> Bob: Hi!
Alice <- Bob: How are you?

12.3. PlantUML

PlantUML is (in its own words) an

open-source tool that uses simple textual descriptions to draw UML diagrams.

See also sections PlantUML Installation and PlantUML Emacs Mode below.

12.3.1. PlantUML Usage

The PlantUML Language Reference Guide is available locally and describes the standard PlantUML diagrams. However, some diagrams are only explained at the PlantUML web site.

Since PlantUML is the standard documentation tool for UML diagrams, specifying diagrams is explained, where the UML diagrams are described.

Here are just some experiments with special diagram types.

Salt is meant for GUI specifications:

@startuml
salt
{#
   Just plain text     | {  () Unchecked radio | [] Unchecked box
                            (X) Checked radio  | [X] Checked box  }
   .                   | "Enter text here"
   [This is my button] | ^This is a droplist^
}
'The only difference between actor
'and participant is the drawing
@enduml

12.3.2. PlantUML Installation

PlantUML is available as a ubuntu package:

apt-get install plantuml

Besides numerous other integrations there is also a sphinx extension module available:

apt-get install python-sphinxcontrib.plantuml
apt-get install python3-sphinxcontrib.plantuml

The standard ubuntu packages are too old (sphinx: 0.5)! Use the backported versions (sphinx: 0.11) which are available in the local repository:

deb [trusted=yes] http://scherer.wiedenmann.intern/repository xenial main
deb-src [trusted=yes] http://scherer.wiedenmann.intern/repository xenial main

12.3.3. PlantUML Emacs Mode

There is also a major mode for editing PlantUML sources in Emacs with preview facilities:

wget https://raw.githubusercontent.com/skuro/plantuml-mode/master/plantuml-mode.el

This mode is included in the local shared site-lisp.

12.3.3.1. Enable the Major Mode

You can automatically enable plantuml-mode for files with extension .plantuml by adding the following to your .emacsrc:

;; Enable plantuml-mode for `PlantUML`_ files
(add-to-list 'auto-mode-alist '("\\.plantuml\\'" . plantuml-mode))

Of course, you can always enable manually the major mode by typing M-x plantuml-mode once in the desired PlantUML file buffer.

12.3.3.2. Emacs Mode Usage

You can tell plantuml-mode to autocomplete the word before the cursor by typing M-x plantuml-complete-symbol. This will open a popup with all the available completions as found in the list of keywords given by running PlantUML with the -language flag.

To render the PlantUML diagram within Emacs, you can hit M-x plantuml-preview. This will run plantuml(1) and display the result in the *PLANTUML-Preview* buffer. The format used to render the diagram is automagically chosen from what’s supported by your Emacs. It will be one of the following, in order of preference:

  • SVG
  • PNG
  • Unicode ASCII art

The diagram will be either created from the selected region if one is available in the current buffer, or using the whole buffer otherwise.

If you want to force a specific output format, you can customize the variable plantuml-output-type to the value you prefer.

12.3.3.3. Default Key Bindings

The following shortcuts are enabled by default:

C-c C-c  plantuml-preview: renders a PlantUML diagram from the current buffer in the best supported format

C-u C-c C-c  plantuml-preview in other window

C-u C-u C-c C-c plantuml-preview in other frame

12.3.3.4. UMLX

The python module UML annotations - line_diversion extracts marked annotation lines from source files.

12.3.3.5. Emacs Mode Installation

To enable preview you need to tell plantuml-mode where to locate the PlantUML JAR file. By default it will look for it in ~/plantuml.jar, but you can specify any location with:

M-x customize-variable<RET>
plantuml-jar-path<RET>

12.4. yUML

yUML is a web service only, which makes it hard to use offline.

There is an unofficial command line tool for yuml and sphinx integration sphinxcontrib-yuml.

GitHub - aivarsk/scruffy is an abandoned project that does not use the web service.

yUML provides support for class, activity and usecase diagrams:

.. yuml::
   :alt: [Customer]->[Orders]
   :type: class, activity or usecase
   :scale: positive integer value
   :direction: LR, TD or RL (ignored by usecase and activity)
   :style: nofunky, plain, scruffy

   [Customer|-forname:string;surname:string|doShiz()]<>-orders*>[Order]
   [Order]-[note:Aggregate root (nofunky){bg:wheat}]
[Customer]->[Orders] nofunky

[Customer]->[Orders] plain

[Customer]->[Orders] scruffy


[Customer]-(Buy Products)
(Make Coffee)

12.4.1. Installation

yUML command line tool:

easy_install git://github.com/wandernauta/yuml

The updated package python-sphinxcontrib.yuml is in the local package repository and can be installed with:

apt-get install python-sphinxcontrib.yuml

See also njouanin/sphinxcontrib-yuml;

Manual installation from original sources:

wget -q 'https://pypi.python.org/packages/bf/9b/99d1ea03b6199ccd93fbf19b02fe23160b0004b6973d4bd1ef233bd633e2/sphinxcontrib-yuml-0.3.1.tar.gz'
tar -zxf sphinxcontrib-yuml-0.3.1.tar.gz
(
cd sphinxcontrib-yuml-0.3.1/sphinxcontrib/ || exit 1
patch -p0 <<'EOF'
diff -u yuml.orig.py yuml.py
--- yuml.orig.py    2013-11-19 22:08:16.000000000 +0100
+++ yuml.py 2018-04-09 10:47:29.740133938 +0200
@@ -15,6 +15,7 @@
 from os import path
 from docutils import nodes
 from docutils.parsers.rst import directives
+import docutils.parsers.rst.directives.images
 from sphinx.errors import SphinxError
 from sphinx.util import ensuredir, relative_uri
 try:
@@ -48,13 +49,13 @@
            :type: class, activity or usecase
            :scale: positive integer value
            :direction: LR, TD or RL
-           :style: boring, plain, scruffy
+           :style: nofunky, plain, scruffy

            [Customer]->[Billing Address]
     """
     type_values = ('class', 'activity', 'usecase')
     direction_values = ('LR', 'RL', 'TD')
-    style_values=('boring', 'plain', 'scruffy')
+    style_values=('nofunky', 'plain', 'scruffy')

     def type_choice(argument):
         return directives.choice(argument, YumlDirective.type_values)
EOF
cp yuml.py /usr/lib/python2.7/dist-packages/sphinxcontrib/
)

12.5. Other UML Tools

Other interesting UML tools by category.

12.5.1. Declarative

  • yUML is a web service.

  • UMLGraph

    Class diagrams: Java syntax, Javadoc integration, umlgraph -> dot -> image.

    Sequence diagrams: pic macros -> pic2plot -> image.

12.5.2. GUI with UML standard support

According to the list of Unified Modeling Language tools on Wikipedia, The most recently active projects are

12.5.3. Generic Diagrams

  • Dia (package dia),

    Freeform diagrams, proprietary compressed XML format. Image, dot, Visio export.

12.5.4. Auto generators

Generating a PlantUML definition of global variables and functions can also be as simple as:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class_name = 'global'

func_type = type(run)
func_type_builtin = type(issequence)
module_type = type(os)

globals_ = globals()
public = globals_.get('__all__', [])
if not public:
    public = list(dkeys(globals_))

items = []
items.extend(((_sym, _val, '-') for _sym, _val in sorted(ditems(globals_)) if _sym not in public))
items.extend(((_sym, globals_[_sym], '+') for _sym in public))

opt_all_syms = None

classes = []
variables = []
functions = []
for _sym, _val, _visibility in items:
    if not opt_all_syms:
        if _sym.startswith('_'):
            continue
        if _sym.startswith('__') and _sym.endswith('__'):
            continue
        if isinstance(_val, module_type):
            continue
    if isinstance(_val, type):
        classes.append((_sym, _val, _visibility))
        continue

    if isinstance(_val, (func_type, func_type_builtin)):
        _list = functions
        _sfx = '()'
    else:
        _list = variables
        _sfx = ''
    _list.append(sformat('{0} : {1}{2}{3}', class_name, _visibility, _sym, _sfx))

printf(sformat('     class {0} << (G, #FFCCCC) >>', class_name))
for _list in variables, functions:
    printf('\n'.join(('         ' + _e for _e in _list)))

Or as fancy as:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
PARENTHESES = dict((
    ('(', ')'),
    ('[', ']'),
    ('{', '}'),
    ('<', '>'),
    ('<<', '>>'),
    ('|', '|'),
    (' ', ' '),
    ('"', '"'),
    ("'", "'"),
))

__all__.append('enclose')
def enclose(elt, open=None, close=None, sep=None, forced=None):      # ||:fnc:||
    r"""enclose string in parenthesis.

    :returns: enclosed string

    :param elt:    element to be enclosed
    :param open:   opening parenthesis (default: `(`)
    :param close:  closing parenthesis (default: :data:`PARENTHESIS`.get(open, '')
    :param sep:    separator after open and before close, (default: '')
    :param forced: add parentheses, even if they already exist.

    >>> printf(enclose('abc'))
    (abc)

    >>> printf(enclose('(abc'))
    (abc)

    >>> printf(enclose('abc)'))
    (abc)

    >>> printf(enclose('(abc)'))
    (abc)

    >>> printf(enclose('(abc)', forced=True))
    ((abc))

    >>> printf(enclose('abc', '<<'))
    <<abc>>

    >>> printf(enclose(enclose('abc', ' '), '<<'))
    << abc >>

    >>> printf(enclose('abc', '-| '))
    -| abc

    """

    if open is None:
        open = '('
    if close is None:
        close = PARENTHESES.get(open, '')
    if sep is None:
        sep = ''

    enclosed = []
    if forced or (open and not re.match('\\s*' + re.escape(open), elt)):
        enclosed.append(open)
        if open:
            enclosed.append(sep)

    enclosed.append(elt)
    if forced or (close and not re.search(re.escape(close) + '\\s*$', elt)):
        if close:
            enclosed.append(sep)
        enclosed.append(close)
    return ''.join(enclosed)

__all__.append('join_lists_flatten')
def join_lists_flatten(lists, sep):                       # ||:fnc:||
    r"""Insert separator between elements of a list.

    :returns: list where each group of elements is separated from the next
      group of element with `sep`.

    :param lists: a list of element groups (lists). All elements of
      group are appended as single elements to the result list.
    :param sep: separater inserted betwwen two groups of elements.

    """
    result = []
    if lists:
        result.extend(lists[0])
    for _list in lists[1:]:
        result.append(sep)
        result.extend(_list)
    return result

__all__.append('plantuml_format_class')
def plantuml_format_class(name, sections=None, circle=None, stereotype=None, alias=None, sep=None): # ||:fnc:||
    r"""
    :returns: class formatted for a `PlantUML`_ class diagram.

    :param name:       class name
    :param sections:   list of class sections, each section is a list of attribute/operation lines.
    :param circle:
    :param stereotype:
    :param alias:      class alias

    .. _`PlantUML`: http://plantuml.com
    """

    if sections is None:
        sections = []

    formatted = []

    _class_param = []
    if circle and circle.strip():
        _class_param.append(enclose(circle, '('))
    if stereotype and stereotype.strip():
        _class_param.append(stereotype)
    class_param = ' '.join(_class_param)
    if class_param:
        class_param = enclose(class_param, '<<', sep=' ')

    if alias:
        name = enclose(name, '"')
        class_alias = 'as ' + alias
    else:
        class_alias = ''

    if sep is None:
        sep = ''

    formatted.append(sformat('class {0} {{', ' '.join((_s for _s in (name, class_alias, class_param) if _s))))

    _first = True
    for _list in sections:
        if _list:
            if _first:
                _first = False
            else:
                formatted.append('  ' + sep)
            formatted.extend(('  ' + _e for _e in _list))

    formatted.append('}')
    return '\n'.join(formatted)

class_name = 'global'

func_type = type(run)
func_type_builtin = type(issequence)
module_type = type(os)

globals_ = globals()
public = globals_.get('__all__', [])
if not public:
    public = list(dkeys(globals_))

items = []
items.extend(((_sym, _val, '-') for _sym, _val in sorted(ditems(globals_)) if _sym not in public))
items.extend(((_sym, globals_[_sym], '+') for _sym in public))

opt_all_syms = None

import pyjsmo
classes = []
variables = pyjsmo.OrderedDict((
    ('-', []),
    ('+', []),
))
functions = pyjsmo.OrderedDict((
    ('-', []),
    ('+', []),
))
for _sym, _val, _visibility in items:

    if not opt_all_syms:
        if _sym.startswith('_'):
            continue
        if _sym.startswith('__') and _sym.endswith('__'):
            continue
        if isinstance(_val, module_type):
            continue

    if isinstance(_val, type):
        classes.append((_sym, _val, _visibility))
        continue

    if isinstance(_val, (func_type, func_type_builtin)):
        _list = functions[_visibility]
        _sfx = '()'
    else:
        _list = variables[_visibility]
        _sfx = ''
    _list.append(sformat('{0}{1}{2}', _visibility, _sym, _sfx))

variables = join_lists_flatten([_v for _v in dvalues(variables) if _v], '..')
functions = join_lists_flatten([_v for _v in dvalues(functions) if _v], '..')

printf(plantuml_format_class(class_name, (variables, functions), circle='G, #FFCCCC', sep='--', alias='G'))

12.6. Summary

  • UML is the abbreviation of unified modeling language. It is a standarizied method for descriping programms.

  • The UML diagrams are easy to understand even for noprogrammers

  • There are existing some UML GUI-TOOLS. There are good for noprogrammers to modeling something, but programmers should’nt use them, because the not-diagram-output is’nt readable for a human. And if the output is not readable, it needs more effort to write or update a documentation (you would need every time this GUI-Tool if you want to know what happends in the programm via UML, without the GUI-TOOL, the output is worthless)

  • Programmers should use PlantUML to create UML diagrams.

    • PlantUML is a open source tool, which uses a certain syntax to create a UML diagram
    • Programmers don’t need the diagram output, to get an accurate overview of the programm, because it is quite easy to understand the associations of the programm if you look at the PlantUML syntax
    • PlantUML is already integrated in a lot of tools (for instance emacs, sphinx), so you can write your PlantUML code in for example in your python script as a commend, and emacs can show you the diagram with a shortcut.
  • A class diagram (similar to entity relationship diagrams for databases) shows boxes for class, interface, annotation, enum, …

    The upper section shows attributes, the lower section contains operations (methods).

    • every class in it’s own box, globals in a own box
    • different types of interfaces:
      • Association – is independent (no owner) (students, teacher)
      • Aggregation o– independent, no owner (department, teacher)
      • Composition *__ death relationsship, if parent dies, all children will die too (kettle and tableau)
  • Sequence Diagram

    Shows the message flow between several participants of a system on timelines.

  • Activity Diagram (in German known as “Aktivitätsdiagramm”)

12.6.1. Object diagram with dot(1)

Why dot(1) is not so useful for object diagrams.

digraph "TCP connection" { // rankdir=LR; Client [shape=plaintext,label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> <TR><TD VALIGN="TOP" ALIGN="CENTER" BGCOLOR="#cccccc"><FONT FACE="FreeSansBold" POINT-SIZE="11"> Client </FONT></TD></TR> <TR><TD VALIGN="TOP" ALIGN="LEFT" BALIGN="LEFT" BGCOLOR="#44cc44" PORT="NIC"> <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="0"> <TR><TD VALIGN="TOP" ALIGN="CENTER"><FONT FACE="FreeSansBold" POINT-SIZE="11"> NIC </FONT></TD></TR> </TABLE> </TD></TR> <TR><TD VALIGN="TOP" ALIGN="LEFT" BALIGN="LEFT" BGCOLOR="#dddddd"> <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> <TR><TD PORT="IP" VALIGN="TOP" ALIGN="LEFT">IP address<FONT FACE="FreeMono" POINT-SIZE="8"> </FONT></TD></TR> <TR><TD PORT="MASK" VALIGN="TOP" ALIGN="LEFT">netmask<FONT FACE="FreeMono" POINT-SIZE="8"> </FONT></TD></TR> </TABLE> </TD></TR> </TABLE>>,style=solid]; Server [shape=plaintext,label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> <TR><TD VALIGN="TOP" ALIGN="CENTER" BGCOLOR="#cccccc"><FONT FACE="FreeSansBold" POINT-SIZE="11"> Server </FONT></TD></TR> <TR><TD VALIGN="TOP" ALIGN="LEFT" BALIGN="LEFT" BGCOLOR="#44cc44" PORT="NIC"> <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="0"> <TR><TD VALIGN="TOP" ALIGN="CENTER"><FONT FACE="FreeSansBold" POINT-SIZE="11"> NIC </FONT></TD></TR> </TABLE> </TD></TR> <TR><TD VALIGN="TOP" ALIGN="LEFT" BALIGN="LEFT" BGCOLOR="#dddddd"> <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> <TR><TD PORT="IP" VALIGN="TOP" ALIGN="LEFT">IP address<FONT FACE="FreeMono" POINT-SIZE="8"> </FONT></TD></TR> <TR><TD PORT="MASK" VALIGN="TOP" ALIGN="LEFT">netmask<FONT FACE="FreeMono" POINT-SIZE="8"> </FONT></TD></TR> </TABLE> </TD></TR> </TABLE>>,style=solid]; // &#9205; // &#9654; // &#11208; Client:NIC -> Server:NIC [label="connects >"]; Client:NIC -> Server:NIC [label="sends\nrequest >"]; Server:NIC -> Client:NIC [label="sends\nresponse >"]; }


[1]As long as programmers maintain documentation, it will always be more or less out of date. When documentation is decoupled from a program it is nothing but guaranteed to be become incorrect.