Question

I have modelled a class structure, where subclasses Rectangle and Circle inherit from an abstract superclass Figure. All subclasses share an interface IGeometry that provides for getArea() and getPerimeter() and needs a different implementation for each subclass. Several concrete classes hold the respective methods, e.g. CircleGeometry:

Class Diagram

For example the class Circle now inherits from Figure and has a CircleGeometry as an attribute.

What is the most reasonable way to model this relationship?

  • If I model this relationship as an aggregate-relationship, where Figure has an IGeometry-object, and then the concrete aggregates for the subclasses are modelled again, i.e. Circle has CircleGeometry, the aggregation would be in effect modelled twice in the subclasses, once inherited, once concrete (see my diagram).
  • If I just model the aggregation on the superclass, but already know which subclass will hold which implementing class, I don't know how to describe their relationsship.

What if the implementing classes are abstract, and the methods for calculating area and perimeter are static?

Was it helpful?

Solution

What is your design intent ?

First of all, your overall design is very close to a the Bridge design pattern, with Figure being an abstraction, and the IGeometry implementations the implementors:

  • The purpose of this pattern is to decouple the abstraction and its implementation in a way both could vary independently. A typical example would be to have your abstract figures, but have different implementations, for example a set of 2D geometries and a set of 3D geometries.

  • The pattern doesn't require a specialization of IGeometry for each kind of the figures, but it's your right to do so if you want to reduce the risk of using the wrong implementor.

Is this really what you want? This great flexibility comes at the cost of a great complexity with lots of boilerplate code that forwards in the abstraction calls to the implementor, and the risk of over-engineering when drawing the frontier between the abstract-abstraction, and its abstract-implementation.

GoF is clear: if there's only one implementor (i.e. in your case, each figure can always have one), then it's "a degenerate case of bridge pattern". So think twice before going that way: it could be a lot simpler if Figure would be an abstract class and each kind of Figure would just have to implement the IGeometry interface directly.

How to express it in UML?

  • If Figure is a superclass you must use the specialization notation in the the diagram: a plain line from the class to its superclass instead of a dotted line, and a white triangle at the end instead of an open arrow head!
  • On the interface side, the dotted line with triangle is ok: it means the implementation of an interface.
  • There is twice the aggregation, because in every shape, you have geometry inherited from the superclass, as well as geo added in the class. This cannot be your intent: you have to make clear that it'sthe same by simply writing geo { redefines Geometry }
  • You can model the relation between the figure and the shape as an aggregation; it's a common practice. But this is not recommended; I've explained in detail why in this other unrelated answer on SO. A simple association with ownership dot is sufficient.

OTHER TIPS

Besides the association, aggregation and composition relations, UML also has a general dependency relation, which is shown as a dashed arrow with an open arrowhead (and often a stereotype indicating the kind of dependency).

Assuming there is a reason why the Geometry hierarchy can't be merged with the Figure hierarchy, I would probably draw it as a composition between Figure and IGeometry, and then a <<creates>> dependency between the concrete figures (i.e. Circle) and geometries (CircleGeometry).
This implies that after creation the concrete geometries can only be used through their interface, so you can't have circle-specific methods in CircleGeometry.

If the Geometry methods are static and you never instantiate a Geometry object, you can model that with a <<uses>> dependency.

I think you are missing the point of interfaces since your use of the interface IGeometry seems quite pointless. By implementing the interface for each figure in a separate figure-specific object you are defeating the purpose of the interface.

The interface should serve as a common type for figures. You want to be able to treat a figure as an IGeometry. With your implementation you cannot do that, in order to call an IGeometry method you need to know exactly what figure you are dealing with and then use the figure-specific implementation of IGeometry. This is useless.

IGeometry methods need to be implemented directly in the figure classes and the abstract Figure class should declare to implement IGeometry. Then you will have gained something: you can call IGeometry methods on a Figure type without knowing what figure you are dealing with.

Licensed under: CC-BY-SA with attribution
scroll top