In a previous post, we have defined a basic set of size and complexity metrics for UML sequence diagrams with SDMetrics. Today, we’ll add a few more sequence diagram metrics that are potentially useful to identify design flaws. I will show how to implement these metrics using the SDMetricsML.
1. Number of “self” messages
The first metric we’ll define counts the number of messages that start and end at the same lifeline. These are messages that the objects send to themselves. The focus of sequence diagrams should be on object interactions. A sequence diagram with a large number of “self” messages may indicate that the modeler attempted to model object internal algorithms. Other diagram types such as activity diagrams are better suited for that matter.
So, how do we identify messages that originate from and end on the same lifeline? A message has a send event and a receive event, each pointing to a message occurrence specification (see SDMetrics’ meta model for UML2). The message occurrence specification has an attribute “covered” that references the lifeline. Therefore, we need to count all messages of the interaction where the send event and receive event cover the same lifeline. That’s a simple enough condition:
<metric name="SelfMessages" domain="interaction"> <projection relset="messages" condition="receiveevent.covered=sendevent.covered" /> </metric>
2. The “height” of a sequence diagram
The next metric we’ll define measures the “height” of a sequence diagram in terms of the maximum number of messages on any of its lifelines. The idea is that very long and “busy” lifelines with lots of messages going in and out may indicate objects that have too many responsibilities.
To tackle this measurement problem, we are first going to count the number of messages each lifeline sends or receives. SDMetrics’ standard set of metrics already defines two helper sets “sendMessages” and “receiveMessages”, which contain the set of outgoing and incoming messages of the lifeline, respectively. All that’s left to do is to add the number of elements in those sets:
<metric name="MsgCount" domain="lifeline" internal="true"> <compoundmetric term="size(sendMessages)+size(recvMessages)" /> </metric>
Then, we simply take the maximum MsgCount value over all lifelines of the interaction:
<metric name="Height" domain="interaction"> <projection relset="lifelines" sum="MsgCount" stat="max"/> </metric>
If you’re concerned about a metric’s mathematical properties and don’t like metrics that take the maximum of anything, you can use metric “MsgCount” for lifelines itself: just delete the internal=”true” attribute in the metric definition. The measurement values will then show up in the output, giving you the “height” of each individual lifeline.
3. Nesting depth of combined fragments
Combined fragments in sequence diagrams can be nested. For example, we could have a critical fragment that includes, among other things, an alternative which in turn contains a loop. This is similar to the nesting of modules, functions, loops, conditions, and so on in ordinary programming languages. And as in plain programming, excessive nesting of combined fragments makes sequence diagrams harder to read and understand. Such diagrams are candidates for refactoring: extract some of the combined fragments into sequence diagrams of their own, and reference them via UML “interaction uses”.
Measuring the nesting of combined fragments is a bit tricky because a fragment does not directly own its sub-fragments. Instead, a combined fragment owns one or more interaction operands (via attribute “operands”), which in turn own the nested combined fragments (via attribute “fragments”). Therefore, we are first going to define a helper set “NestedCF” for combined fragments, which gathers all the combined fragments of its interaction operands:
<set name="NestedCF" domain="combinedfragment"> <projection relset="operands" target="interactionoperand" set="fragments"/> </set>
Next, we can use the “nesting” attribute of projections to calculate the nesting level for each combined fragment:
<metric name="CFNesting" domain="combinedfragment" internal="true"> <projection relset="NestedCF" nesting="true"/> </metric>
Metric CFNesting yields 0 for combined fragments without any sub-fragments, 1 for combined fragments with one level of sub-fragments, and so on. Last, we take the maximum nesting level over all combined fragments in the interaction:
<metric name="CombinedFragmentNesting" domain="interaction"> <subelements target="combinedfragment" sum="CFNesting" stat="max"/> </metric>
That’s it, the depth of nesting of combined fragments in interactions. I will add the metric definitions in this and the previous post to SDMetrics’ standard set of metrics for the next release of SDMetrics. However, you can also calculate them with older versions of SDMetrics (2.0 or later).