Last autumn I posted a personal UML style guide, followed soon afterwards by a detailed explanation of my reasoning for not including associations. Those articles had quite a positive reception (thanks), but patterns and heuristics can only go so far. So this article presents a (long) worked example to show my UML style in action. I make no claims for the resulting model – except that it works for me, and seems to satisfy those who are able to persuade me to create UML models. I should also point out that I usually – ie. when I have the opportunity – jump right to the last page; what you see here is one conceptual route to that end point…
Fade to an imaginary office somewhere in the north of England. It’s the end of an imaginary project in which we’ve developed an XP planning game tool in Java (this example is based on the Planning Game Simulator exercise in Bill Wake’s excellent Refactoring Workbook). The customer has scheduled a story to document any “interesting” parts of the design, as a roadmap for the code’s future developers and maintainers. We select the tool’s status bar, and we decide to draw a UML model to convey the following information:
The status bar always displays a summary of the plan (number of cards, their total cost, current velocity, and number of iterations required to complete the backlog). Whenever a card is added to or removed from the backlog, or when a card’s cost changes, or when a card is split, the Plan re-calculates everything and the status bar updates itself to show the new information. The status bar does this by listening for change events thrown by the Backlog; when one occurs it requests the latest information from the Plan and displays it.
We begin by drawing a static model showing the classes involved, with their relationships, methods and packages. This is the kind of model created by one of those reverse engineering tools that reads our code and constructs a thorough model of it all:
Note that I have not shown attributes; nor have I shown every method. They aren’t relevant (and I got bored typing). Note also that it is a big stretch from this model to the above outline description. In my opinion this kind of model tells us loads of unimportant stuff and very little that’s genuinely useful. Let’s see what we can do about that. First let’s correct those associations so they show the correct navigability:
That already helps, because it removes some implied, and incorrect, information from the diagram.
Now, the inheritance relationship between
JLabel is an implementation detail. All we really need to know is that the
View package makes use of Swing. We can say something similar about the use of
Observable: what’s important is the application of the Observer design pattern, wherease the use of those two specific classes is an implementation detail. Where a dependency follows a well-known pattern, I’ll tend to use a stereotype to indicate the fact.
That already seems much clearer to me (and note that I was also able to get rid of that
update() method on
PlanSummary – again, it’s part of the implementation of the Observer-Observable relationship, which is already completely described by that stereotype.
Next, I notice that the list of methods on
Backlog add nothing to the story told by the diagram:
I’ve also replaced the methods on
Plan, instead listing attributes as a way of showing the Plan’s responsibility to know these things. It feels dirty to do so, and runs counter to my principles; but right now they do convey the information that the
Plan has a velocity distinct from the Backlog.
Note that I also removed the spurious multiplicities on the non-navigable ends of those associations. I wouldn’t want my reader to infer that a Card can belong to only one Backlog, for example. The code doesn’t implement that restriction, so the original model was incorrect to include these.
At this point it could be argued that the Product-Backlog and Backlog-Card associations should be depicted using one of the containment or ownership adornments. But I find that too to be an implementation detail. True, the Plan’s constructor creates a Backlog instance, but that’s surely a detail of the code. In domain terms, it is perfectly reasonable to “construct” a plan around an existing backlog, and a different sequence of refactorings might have brought this code to such a structure. The adornment would only serve to constrain the model, and thereby to increase the probability that it might need to change if the code were refactored in the future.
And now I’m going to remove the remaining multiplicities too! This is a static model; the entities shown are all classifiers. But those multiplicities count instances, which I find to be incongruent. More importantly, I don’t believe they add anything to the story told by the picture. And so we are left with dependencies, pure and simple.
And so, at last, we come to
BacklogEvent. Clearly there is no such concept in the domain (Ken Schwaber’s books don’t mention BacklogEvents, for example). It was included in the model simply because it exists in the (current) implementation: Every time the Backlog is changed it creates a new
BacklogEvent instance to convey both the event type and the affected Card (via the Observer-Observable mechanism) to anyone who’s listening. The only reason we require a
BacklogEvent class is because the Java implementation of the observer pattern passes only a single argument back to the listener via the notification method. Some part of the planning game’s code needs to get both the action and the affected Card, hence the need for this little bundle.
Does the diagram convey all of that last paragraph? Or indeed any of it? No. Does it need to? Again, I would argue that it doesn’t. We can’t come close to describing all of that in the diagram, and anyway it is all in the code. So my final static model would be the following:
If pressed, I might concede that
BacklogEvent is an interesting concept at some level of detail. But how to convey everything we said above? My preferred approach, to which I will only resort under duress, is to show the
BacklogEvent as a kind of association class. The class is the “message type” within that dependency, and at the same time acts as the medium for those messages. Marshall McLuhan would be proud…
I’ve also taken this opportunity to move the classes around so that the dependencies all flow rightwards or downwards, following the reader’s eye. Its only a convention, but I now find it easier to follow the flow of delegation among the classes.
So, we now have a static model that, in my opinion, conveys only what’s important and nothing else. But is that all we can do? It still isn’t easy to read the original story from this model – because the story was dynamic and this model is static. And so I offer the following instead:
I could argue that either the static model or this collaboration model is sufficient to depict the original story. But I also note that the text version took significantly less time to create…