1

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?

  • Hello and wlecome! dared to make a few minor editorial edits, to keep the title short and expressive, summarize your problem, and clarifying the question. I hope I got it right. Don't hesitate to edit or reverse if you think I didn't get it right :-) – Christophe Aug 25 at 7:48
  • 1
    Hey @Christophe, thank you very much for the warm welcome and your time answering and improving my question. – kiloton Aug 25 at 18:06
3

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.
| improve this answer | |
  • 1
    I only now realized that my picture doesn't have Square in it, which would also contain the RectangleGeometry. The "generalization" arrows are missing out of negligence, you are absolutely right that I should've rearranged that, sorry. I will edit that and switch the image. I learned a lot from your answer, and think it answers my original question. I will go with your first suggestion of having the subclasses implement IGeometry directly. Originally, I had IArea, IPerimeter and other interfaces seperated, where this aggregation made more sense. Cheers! – kiloton Aug 25 at 18:28
  • 1
    @kiloton Don’t be sorry :-) This is real life! Yes reuse of some implementors is one of the possibility suggested by GoF for the Bridge. I didn’t mentioned it, because it would have immediately raised the issue of Rectangle/Square vs Liskov Substitution Principle. I see, in your original design you splitted even further, adopting almost a mixin approach, or perhaps Entity (figure), Component (behavior) System design. This can make sense for more complex behaviors and more dynamic problems. But for simple Figure, the simple abstract class approach is certainly the best – Christophe Aug 25 at 18:51
1

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.

| improve this answer | |
  • Hey @Bart, thank you for answering as well. I could also learn some new concepts from your answer, and will merge Geometry and Figure hierarchies like you suggested. – kiloton Aug 25 at 18:34
0

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.

| improve this answer | |
  • Hey @Martin Maat, thanks for answering. Please see my comment on the accepted answer and on why I had implementing classes at that point - I started out with several different interfaces for different methods, as in IArea, IPerimeter and IInfo and wanted to composite my Figures. I have since merged the hierarchies. – kiloton Aug 28 at 11:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.