Over on my personal UML style guide Yanic recently asked “I’m wondering why you prefer collaboration diagrams over sequence diagrams?”. Oops, I never really explained that — so here goes…
The main point about my UML style is to make it clear to the reader that the model is a sketch. What I care about with dynamic models is the division of responsibility among objects; I want to show who does what in carrying out this one system responsibility. But I am rarely interested in describing how those objects do their thing; which means that I don’t care to model:
the exact timing of messages and responses;
the type and content of message parameters;
the type and content of message responses;
what other activities the objects get up to before, during or after the collaboration I’m currently describing.
To my mind, a collaboration diagram is more suited to this style, for at least the following reasons:
A collaboration diagram shows objects, whereas an interaction diagram focuses on timelines. I want to depict objects playing roles in a story; timing and the flow of time is (almost) never relevant for that. When whiteboarding or using CRC cards, I want to be able to point to objects; I want to be able to identify with, and play the part of, objects. Tangible, autonomous, anthropomorphic, responsible actors in the story; not column headings in a flowchart.
Those timelines can be interpreted as implying things about the focus of attention of the objects, particularly when they show “method bodies” as blocks of activity in response to an incoming message. It could then be assumed that the model intentionally says something explicit about such things, which I (almost) never mean to do.
I think of the “messages” on a collaboration diagram as indicating delegation of responsibility; whereas the explicit time axis and the receiver blocks in an interaction diagram can make them seem to be actual messages (or worse, method calls).
The diagram names are indicative: “Collaboration” is about apportioning responsibility; “interaction” is about sending and receiving. The latter seems to me to be at a lower level of abstraction.
I know that one can produce an interaction diagram and a collaboration diagram that are logically equivalent, and so my reason for avoiding the former is, in the end, a matter of personal preference. I like models that say only what I want them to say, without implying anything else. Less is more.
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: Continue reading →
A month ago I posted a personal UML style guide, without going into very much background as to why I had arrived at that particular subset and that particular usage. And so I’ve been asked to “explain the reasoning” behind point 5:
“I use only two kinds of relationship on a class diagram: «implements» and «uses». No containment or aggregation (even when describing C++ code), no bi-directional relationships, no roles or multiplicities, and no navigability.”
Looking at it again, I can see it does need some explanation! The statement is based on a whole raft of inter-related beliefs that I’ve held for a very long time, and which may require several articles to unravel. Anyway, here’s a beginning…
Firstly, I should reiterate the goals I have when creating a UML model. I use UML under only two circumstances:
As a design tool, to sketch the main collaborators in a scenario we’re about to implement. This happens very frequently during a project, and is a 3-minute process of sketching on a whiteboard or the back of an envelope. The goal is to develop an outline understanding of how to assign responsibilities among the objects involved in one aspect of a system. This model is almost always a collaboration diagram, and will be thrown away very soon after it was drawn.
To create a map of a system as an aid to future travellers. I prefer to let the code and the tests do the talking, and so these after-the-fact UML models are intended only as signposts and summaries. Again, I focus on the system’s behaviour, preferring collaboration diagrams, object state diagrams and swim-lane activity diagrams. I do usually include one or two static models, but each will document only a small, tight-knit cluster of classes; I never attempt to use them to show the whole system.
I use UML a lot in my practice. Not the UML, but a UML – eclectic and variable notation, sketched on whiteboards and cards, and scrubbed out immediately. But recently I’ve actually had to make a UML model to go in a document, so I thought I would share a few lessons I’ve learned about UML over the years…
It’s okay to use UML to document code, as a navigational aid or to give a 30,000-feet summary for folks who can’t read the code.
But only use it to describe stable aspects of the code. Don’t try to hit a moving target, or to describe code that doesn’t exist.
Divide the model into pieces, each of which tells a single story. It can be very tempting to try to get everything onto one huge static model, but when the aim is to communicate smaller focused models work better.
Prefer dynamic models, particularly collaboration diagrams and state transition models. Where multiple threads or processes interact, use activity models (with swim-lanes) to show timing and synchronisation. Static models (particularly class diagrams) often give the (false) impression of “structure” – whereas a running system is all about behaviour. So focus the models on the system’s behaviour.
I use only two kinds of relationship on a class diagram: «implements» and «uses». No containment or aggregation (even when describing C++ code), no bi-directional relationships, no roles or multiplicities, and no navigability.
Follow each static model with a textual traversal of the diagram: Write a single paragraph for each class, consisting of a 1-sentence description of the class’ role in the model (not the system), followed by a 1-sentence description of the job done by each of the dependencies leaving that class. The result will be a simplified narrative that walks through the system’s static structure in a dynamic way.
When deciding what to include on a class diagram, begin with the classes that are essential to telling the story of this particular part of the system. Then add in the “hard” structure, ie. the «inherits» relationships and those «uses» relationships that are really «creates». Then try to remove classes until the model is as simple as possible; only add further dependencies if they carry delegation that’s essential to telling the story.
Don’t be afraid to mix diagram types. I’m particularly prone to putting messages on my class diagrams, for example to show system start-up or incoming asynchronous stimuli.
Lay out each model, whether static or dynamic, so that it reads naturally from left to right and from top to bottom. This will usually mean that «inherits» relationships point up the page (abstract above concrete) and «uses» relationships tend to point to the right. In fact I find that left-pointing dependencies usually only arise to represent the “call-backs” in some variant of the Observer pattern.
Try to arrange for one of the models to describe the entire system using 4-6 classes.
These aren’t “rules” as such, and I don’t consciously use them as a check-list or style guide. But when I’ve made a model I’m happy with, I find it always has all of the above properties. Does a similar style work for you? What, if anything, do you do differently?
I’ve now posted a worked example of this style of UML modelling.
The standard three- or four-layer models of application architecture seem to dictate the direction of the dependencies between the various “kinds” of object in the system: The UI depends on the application layer, because the UI “drives” what happens “below”. The application layer depends on the business objects, which do all the domain-specific things. The business objects use (and hence depend on) the persistence layer and comms layer, both of which in turn use and depend on external APIs. It is perfectly natural to read a layered model in this way – indeed, that was its purpose.
And it is this very interpretation that has made so many applications difficult or impossible to test. Each of those perceived dependencies ends up enshrined in the code structure, and by stealth the design has cast in concrete the layered model. At which point someone like me comes along an exhorts the developers to improve their test coverage. I’m told: “We tried that, but it takes too long to manage the Oracle testdata we’d need to test the GUI,” or something similar. Or the business decides to switch to a new database or enterprise comms architecture, requiring huge swathes of business logic to be re-engineered.
Yesterday I was speaking to a chap who had just completed a 5-day UML course. He’s a manager, and I asked him why he had taken the course. “Well now I can at least read the designs people do on my projects. And hopefully I can now also do some design myself.”
I’ve heard these misconceptions many times before – that UML is either a design tool or (worse) a design process. It isn’t. It’s a notation, based on an underlying set of concepts. Knowing UML does not make one capable of design, any more than one can become a chef simply by recognising food types and kitchen utensils.
UML has become an industry in itself. I can understand the commercial forces that caused it to happen, but I can’t help feeling that the resulting situation contains more negatives than positives. Sure, vendors can ply standards-conformant consultancy, CASE tools and training courses, but far too many people think that UML brings an essentially creative and intellectual skill (by which I mean software design) into the broader world. “Knowing” UML does not de-skill analysis or design. Knowing the UML notation does not make one a designer. But not knowing that does make one dangerous.