The book Head First Design Patterns presents the following UML as an example of the Observer pattern:
What strikes me in this diagram is the association relationship between the Subject
and Observer
interfaces. As far as I understand Java interfaces, they cannot implement a "Has-a" relationship in this way.
When I look at the implementation example provided a few pages later, I find that sure enough, the interfaces are plain old interfaces:
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers;
}
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
Instead, the association relationship is implemented in the concrete WeatherData
and Display
classes:
public class WeatherData implements Subject {
private List<Observer> observers;
//more code
}
Which does not correspond to the UML and feels a bit wrong to me. Why not just implement Subject
as an abstract class instead? Does the UML spec formalize the idea of associating an interface with another interface?
CodePudding user response:
You are right about your questioning. And let's see why the things are as they are:
- Interfaces represent contracts. In Java, the contract is mainly about a set of methods. In UML, it can be broader, e.g. be about operations (methods), properties (fields), associations (not a java language feature: there are several ways to implement them), or contraints (preconditions, postconditions, invariants, ...).
- Classes have to fulfil the contracts of the interfaces that they realize/implement. In Java, the compiler will only verify that the class implements the methods required by the interface and with the right signature. The developper would nevertheles expect more, i.e. that the java class behaves according to the interface's documentation. In UML, you'd similarly expect that a class provides itself all the features promised by the interface (see here).
- How are the rules be enforced? In Java, you'll have to be explicit: all methods must be provided or you'll get a compilation error. If you'd use TDD, you'd also foresee a set of tests that verifies that an object behaves according to contract of the interface. In UML, you can have ambiguities, because you are not forced to show everything in a diagram. So if you don't show a class feature or an association, it doesn't meant that there is not such thing: the thing could be in the model but not shown in a particular diagram. So if a class implements an interface, you could assume that it provides the elements even if they are not shown in the diagram. If you'd use such an ambiguous diagram for code generation, the code would not compile.
Coming back to your observer:
- the diagram tells us that the
Subject
has a navigable association toObserver
. In the code, you cannot implement nor even directly declare this kind of relationship in the interface. However, the fact that theSubject
interface shall allow to register and removeObservers
, implicitly suggest such an association. - the diagram don't tell us that
WheatherData
has a navigable association toCurrentConditionDisplay
. But in principle, they should, since these classes realizeSubject
andObserver
respectively. This makes the diagram ambiguous. However, if we trust these classes to comply with the interface, we could live without showing the extra associations, just assuming that they have to be here. Moreover, strict UML compliance would require to add 3 such associations here, making the diagram more difficult to read.
Now, as you rightly say, we could implement the design patterns using an abstract class instead of an interface. But since Java does not allow multiple inheritance, this approach would forbid subjects or observers to be in other class hierarchies.
Some additional background
First of all, the observer pattern -- as all patterns in Heads first Design Pattern -- originates from the GoF. Interestingly, GoF predates UML and Java; Its patterns only used class inheritance, if needed with multiple inheritance. When the book mentioned "interface", it was only about the implicit interface exposed by classes, very often abstract classes.
When Java came out, it was then a challenge to transpose all these design patterns, making explicit difference between interfaces (in the Java sense) and abstract classes. This is why you'll often find several variants among class diagrams of these patterns.
Heads first is no exception. The goal of that excellent book is to teach using patterns in coding. The main purpose of its diagrams is therefore to convey the design intent, and not to teach the rigorous use of advanced UML. So they used diagrams that might sometimes be formally ambiguous. Overall I think they made an excellent job.