tags:

views:

65

answers:

2

Let's say you want to diagram a Map class (the mathematical kind, not the cartographic kind). In Ruby, you might have something like:

class Map
  attr_accessor :nodes, :edges
  def initialize
    @nodes = Set.new
    @edges = Set.new
  end
  def minimum_spanning_tree(&mst_algorithm)
    mst_algorithm.call(@nodes, @edges)
  end
  ...
end

When I start diagramming this, my thinking goes as follows:

Let's try a Class diagram since we're talking about classes. I'll create a Map class. And a Node class. And an Edge class. And a Set class. Fine. Now I'll draw a Composed-Of(1:2) line from Map to Set -- one for each @nodes and @edges. Then a has-many(1:0..*) line from Set to Node and another one from Set to Edge. But now I'm saying that each set can have any mix of Nodes and Edges, which isn't true. And it doesn't help to put down two Set elements on the diagram (with two corresponding Composed-Of(1:1) lines), since they're the same object.

So I thought: well, maybe UML wants me to be more C++/Java-ey. A templated Set<Node> and Set<Edge> aren't possible in UML, but I could create subclasses: NodeSet and EdgeSet.

Lastly I considered an Object diagram, but that's not right. I'm talking about the Set class, not individual Set instances.

Is there a better answer? Or have I already found the "least bad" one?

Later

The answers that Marc W and Pete Kirkham are fantastic for the question as I first worded it. The problem is that I was trying do use a simple analogy to my real problem because I can't reveal the problem as it exists. I was really only after the bit about how to have two of the same class that have different relationships, but act the same and have the same attributes (though not attribute values).

Let's try again with some different models: an ActiveDirectory, a Firewall, and two Routers. One router (the LAN one) has references to the ActiveDirectory and the Firewall; the other (the WAN one) has references to the Firewall and some number of public servers (we'll ignore them in this diagram). It's perfectly conceivable that both Routers are of the same make, model, &c. They'll have different serial numbers (the objects are different), but they're definitely both Routers. Yet to put both on a class diagram, I have to subclass Router into LANRouter and WANRouter. The analogy to Marc W's solution is to connect the Firewall and ActiveDirectory directly, leaving the implementation (Router) to the class to determine. But the abstraction must leak if the UML is to be used to actually build the system.

+2  A: 

Don't draw any line from Map to Set. In this case, Set is just a data structure given to you by Ruby holding the objects that really matter. Whether it's a Set, a HashMap, an Array, etc is implementation-specific and doesn't matter for UML. Just make a Composed-of(1:0..*) line from Map to Node and from Map to Edge. After all, in English, your Map really is composed of nodes and edges, correct? The reason you include Map in your diagram is because you are actually the one creating the class.

You're right about the object diagram: it's not necessary in this case. Same with the templating syntax you suggested.

Marc W
That's right. What you can do though is to define that the association end has the properties isUnique=true and isOrdered=false. This indicates that the association does not contain repeated elements and that the elements are not ordered. At the conceptual level, this is a way to represent your "Set behaviour"
Jordi Cabot
Spot on advice: leave collection classes off class diagrams.
chimp
+3  A: 

If you are modelling the domain, rather than the implementation, UML provides stereotypes for the relations. In this case you'd mark the nodes and edges as being {unordered}, which relates to a bag or set, and {unique} which narrows it down to being a Set. I can't see any restrictions on the type of the node or edge, so if it's not there then don't try and add it:

class Map
  +nodes : { unordered, unique } object[0..*]
  +edges : { unordered, unique } object[0..*]
  +minimum_spanning_tree(mst_algorithm:callable)

A good code generator/reverse engineering tool would map { unordered, unique } UML collections to/from Sets in the source code.

You might want to make Map a parametric type in your domain model, if that information is useful to you. But since the information isn't used in the implementation, it's a little hard to see the point, and making things as simple as possible without losing utility is always a goal in modelling.

If you want to document that a particular class is implemented using Sets, you can do that with associations. You're right that C++ or Java allows you to declare the type of the element in the Set, and that these maps to parametric types in UML. AFAIK Ruby doesn't have a mechanism for implementing such type restrictions, so to document a Ruby implementation of your Map, document only what is in the implementation - the Map has two associations to Set, and places no restrictions on what's in the Set. ( you might want to document it as an invariant constraint, that is then unit tested rather than a type constraint, but again it's a bit hard to see why you'd be working in Ruby if you wanted restrictive static type checking )

Pete Kirkham
What if I _am_ trying to diagram for implementation? Say, for example, if my organization does lots of outsourcing and wants to have all sorts of documentation for CYA purposes...
James A. Rosen
I would add a note that { unordered, unique } implies Set.
Pete Kirkham
{unordered} and {unique} are constraints, not stereotypes. But good answer, +1
chimp