Methods In the implementation of the perform() template method in AbstractMove, the second step is to perform the actual move. Within class AbstractMove, this step is meaningless given that an abstract move does not represent any concrete move we could perform. For this reason, we need to leave out the actual execution of the move. However, we cannot leave this step out entirely, because as part of our template we do need to specify that executing the move needs to happen, and needs to happen specifically after the move is pushed to the move stack and before the move execution is logged. In our design we thus specify that this computation needs to happen by calling a method. However, because in Java all methods that are called need to be declared, we must add a new method declaration. In this example I called it execute(), because we cannot give it the same name as the template method (this would result in a recursive call). Because we do not have any implementation for execute(), we can defer the implementation to the subclasses. This is allowed because AbstractMove is declared to be abstract, so there is no issue if the class’s interface is not fully implemented. Although it sometimes makes sense to declare abstract methods to be public, here I declare execute() to be protected because the only classes that really need to see this method are the subclasses of AbstractMove that must supply an implementation for it. Summary of the Pattern The declaration of class AbstractMove, above, illustrates the key ideas of the solution for TEMPLATE METHOD. The following points are also important to remember about the use of the pattern: • The method with the common algorithm in the abstract superclass is the template method; it calls the concrete and abstract step methods; • If, in a given context, it is important that the algorithm embodied by the template method be fixed, it could be a good idea to declare the template method final, so it cannot be overridden (and thus changed) in subclasses; • It is important that the abstract step method has a different signature from the template method for this design to work. Otherwise, the template method would 7.10 Proper Use of Inheritance 181 recursively call itself, quite possibly leading to a stack overflow; following the advice of Section 7.5 about avoiding unnecessary overloading, I would recommend actually using a different name in all cases. • The most likely access modifier for the abstract step methods is protected, because in general there will likely not be any reason for client code to call individual steps that are intended to be internal parts of a complete algorithm. Client code would normally be calling the template method; • The steps that need to be customized by subclasses do not necessarily need to be abstract. In some cases, it will make sense to have a reasonable default behavior that could be implemented in the superclass. In this case it might not be necessary to make the superclass abstract. In our example, there is a default implementation of log() that can be overridden by subclasses. In a different context, it might make more sense to declare this method abstract as well. When first learning to use inheritance, the calling protocol between code in the superand subclasses can be a bit confusing because, although it is distributed over multiple classes, the method calls are actually dispatched to the same target object. The sequence diagram in Figure 7.12 illustrates a call to perform() on a DiscardMove instance. As can be seen, although it is implemented in subclasses, the call to the abstract step method is a self-call. Fig. 7.12 Call sequence in the TEMPLATE METHOD 7.10 Proper Use of Inheritance Inheritance is both a code reuse and an extensibility mechanism. This means that a subclass inherits the declarations of its superclass, but also becomes a subtype of its superclass (and its superclass’s superclass, and so on). To avoid major design flaws, inheritance should only be used for extending the behavior of a superclass. As such,
[1]
Martin P. Robillard,et al.
Sustainable software design
,
2016,
SIGSOFT FSE.
[2]
D. L. Parnas,et al.
On the criteria to be used in decomposing systems into modules
,
1972,
Software Pioneers.
[3]
Elias Pimenidis,et al.
Clean Code: A Handbook of Agile Software Craftmanship
,
2009
.
[4]
David Lorge Parnas,et al.
Software aging
,
1994,
Proceedings of 16th International Conference on Software Engineering.
[5]
Martin Fowler,et al.
Patterns of Enterprise Application Architecture
,
2002
.
[6]
Kristin Decker,et al.
Uml Distilled A Brief Guide To The Standard Object Modeling Language
,
2016
.
[7]
Frank Buschmann,et al.
Pattern-Oriented Software Architecture, a Pattern Language for Distributed Computing
,
2007
.
[8]
Ralph Johnson,et al.
design patterns elements of reusable object oriented software
,
2019
.
[9]
Mauro Pezzè,et al.
Software testing and analysis - process, principles and techniques
,
2007
.
[10]
Bertrand Meyer,et al.
Applying 'design by contract'
,
1992,
Computer.
[11]
Oliver Vogel,et al.
Software Architecture - A Comprehensive Framework and Guide for Practitioners
,
2011
.