1
votes

Various similar-looking questions exist but I could find none that responds to my query.The possibility of having the specified aggregation relationship while implementing an interface is an essential part of my question, which is not treated in other similar questions or their solutions.

My question concerns implementation of Design Patterns in Java. For the ease of explanation, I'm considering the Command design pattern as shown in this diagram.Class Diagram

Since this UML representation requires that "Invoker" should be an interface and that it should also have an N-ary aggregation relationship with the "Command" interface, it's not possible to implement it if I define Invoker as a Java interface because I can't declare non-static non-final attributes in a Java interface and I do need to define a non-final Collection of "Command" objects to establish that aggregation relationship between "Invoker" and "Command".

So, I went ahead and implemented Invoker as an abstract class, which lets me have the aggregation and also provides abstraction to the implementations of "Invoker" but I'm wondering if it is a good design practice because UML does have a stereotype <<abstract>> but the UML class diagram for this pattern explicitly specifies using an interface rathern than an abstract class. I've also done the same for "Subject" interface of "Observer" pattern implementation in Java for a similar reason.

Please let me know if there's a way to keep "Invoker" as an interface in Java and still achieve the aggregation relationship with the specified multiplicity. If what I did is the best way to do it, please let me know if it may have some adverse effects on the structure of a program I build using the pattern this way.

Adding below the class diagram of a short Java implementation of Command Pattern that I put up for enhanced clarity of the question. I've configured "Controller" here as the "Invoker" interface that can be implemented in different ways, e.g. "InfraredRemote Controller" in this diagram. But the Design Pattern requirement of aggregation relationship between "Invoker" and "Command" interfaces made me configure "Controller" as an abstract class because I could not find any way to achieve the required multiplicity and relationship when I configured "Controller" as an interface. Java Implementation

2
@ThomasKilian that question does not treat the relationship between classes as shown in UML Class Diagrams of the standard Design Patterns. The possibility of having the specified aggregation relationship while implementing an interface is an essential part of my question.Donbhupi
I think the diagram doesn't show that there needs to be a ConcreteInvoker that implements all the things because that's not really relevant to the pattern / it's implied. Likewise, there is nothing here that shows that the concrete receiver isn't necessarily the client.zapl
Yes indeed. @zapl, even Receiver would have an implementation, which is intentionally omitted from the diagram for the very same reason that it's an implementation detail abstracted by the interface.Donbhupi

2 Answers

1
votes

The important part in the command pattern is that all ConcreteCommands have a common Command "interface" that hides the entire command logic and all possible Receivers. This turns a bunch of code into an object that anyone can execute without knowledge about the rest of the system. Dependency direction in term of the dependency inversion principle is what matters here.

I would not even make it a hard requirement for an implementation of this pattern to implement the Command "interface" with an interface. The point is that there needs to be something that hides the details. It's a different story if you make a concrete UML system design for a concrete problem to be implemented in Java and you mark the command as <<interface>>.

The relation between Command and Invoker is also not really part of the invoker "interface"(-contract). The client should not care. It merely sees that it can pass commands. The intention of the n-ary aggregation could be to show that an invoker can take any kind / amount of Commands. A depiction of something more or less outside of that pattern that takes Commands & is known to the Client (that relation is also not necessary, commands can somehow find their way to some invoker compare this). The pattern itself does not demand that Invoker is an interface. It could be concrete, since concrete implementations still have an "interface".

One common application of the command pattern is to have an invoker that keeps tracks of applied commands and allows to undo them for example. In that case there is a strong relation between the concrete invoker and the commands, where the invoker stores commands in some for of list. The diagram could as well mean that. You typically don't have that kind of relation in diagrams of the pattern at all. See link above again.

Regardless of what it means I'd say you're not supposed to implement a concrete relation between Command and Invoker inside Invoker in case you decide to make Invoker and interface. That would be more than this diagram suggests. It would also force all invokers to have a specific behavior. And that's not the point of this pattern.

<<interface>> and <<abstract>> stereotypes are only meaningful if the one that designed the diagram actually means the Java equivalent. That's usually not the case when the diagram shows a concept where anything that isn't of relevance would become an interface. The same applies to relations. "Aggregation" can be used very liberally. In case of the invoker<>command relation It can mean that the invoker has seen all the commands, it doesn't have to store them.

1
votes

Thanks to a good brainstorming session with @zapl and some more research on the web, I've found the answer through this article that makes reference to this page of a very interesting book.

“Program to an interface”, really means “Program to a supertype”. – Head First, Design Patterns

Apparently, the use of word 'Interface' in UML specifications of Design Patterns is more generic and only denotes abstraction, viz. supertype, which can be achieved in the Java implementation by using either an Interface or an abstract class. Although the stereotypes for <<abstract>> classes exist in UML, they are rarely used to describe Design Patterns.

And as it's not possible to achieve the n-ary multiplicity between Java interfaces, the UML "Invoker" interface in the example I used in my question needs to be implemented using a Java interface that is implemented by an abstract class and by extending that abstract class with concrete Invoker subclasses. This would maintain malleability of the code while preserving the abstraction as shown in the implementation below: Java implementation

Here the client "Implementer" uses the Java interface which is implemented by a Java abstract class to provide the multiplicity with "Command" interface and the access to that multiplicity to the concrete Invokers; these two together represent the "Invoker" interface in the UML specification of the Command Design Pattern.

I'm leaving my answer open for comments as of now, expecting comments on something I may have missed. If I don't get any, I'll accept my answer in, say, two days.