/*******************************************************************************
* Companion code for the book "Introduction to Software Design with Java",
* 2nd edition by Martin P. Robillard.
*
* Copyright (C) 2022 by Martin P. Robillard
*
* This code is licensed under a Creative Commons
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
*
* See http://creativecommons.org/licenses/by-nc-nd/4.0/
*
*******************************************************************************/
package e2.chapter7;
/**
* Root class for all moves that require a reference
* to the GameModel. Method perform() fulfills the role
* of the Template Method in an application of the Template
* Method design pattern.
*/
public class AbstractMove implements Move {
protected final GameModel aModel;
AbstractMove(GameModel pModel) {
aModel = pModel;
}
public void perform() {
aModel.push(this);
;
log();
}
protected void execute();
private void log() {
System.out.println(getClass().getName());
}
}
The point of declaring a method abstract is so that we can explicitly refer to the abstraction that the method represents without needing to define it concretely in the abstract class. The implementation of the method then gets delegated to the subclasses.
The point of declaring a method abstract is so that we can explicitly refer to the abstraction that the method represents without needing to define it concretely in the abstract class. The implementation of the method then gets delegated to the subclasses.
This method is a . One of the steps in the algorithm
that it implements is a placeholder (namely the call to execute()
,
which is an abstract method.
This method is a . One of the steps in the algorithm
that it implements is a placeholder (namely the call to execute()
,
which is an abstract method.
Declaring the constructor of an abstract class to be protected is the logical choice, because it can only be called via subclasses.
Declaring the constructor of an abstract class to be protected is the logical choice, because it can only be called via subclasses.
This class must be declared abstract because its declaration includes an abstract method.
This class must be declared abstract because its declaration includes an abstract method.
print(String)
and then
println()
.print(String)
and then
println()
.x
- The String
to be printed.Console.charset()
if the Console
exists,
stdout.encoding otherwise.
Console.charset()
if the Console
exists,
stdout.encoding otherwise.
For simple stand-alone Java applications, a typical way to write a line of output data is:
System.out.println(data)
See the println
methods in class PrintStream
.
System
class contains several useful class fields
and methods. It cannot be instantiated.
Among the facilities provided by the System
class
are standard input, standard output, and error output streams;
access to externally defined properties and environment
variables; a means of loading files and libraries; and a utility
method for quickly copying a portion of an array.System
class contains several useful class fields
and methods. It cannot be instantiated.
Among the facilities provided by the System
class
are standard input, standard output, and error output streams;
access to externally defined properties and environment
variables; a means of loading files and libraries; and a utility
method for quickly copying a portion of an array.Class
object.
Class
object.
If this Class
object represents a class or interface,
not an array class, then:
N + '/' + <suffix>
where N
is the binary name
indicated by the class
file passed to
Lookup::defineHiddenClass
, and <suffix>
is an unqualified name.
If this Class
object represents an array class, then
the result is a string consisting of one or more '[
' characters
representing the depth of the array nesting, followed by the element
type as encoded using the following table:
Element Type Encoding boolean
Z
byte
B
char
C
class or interface with binary name N L
N;
double
D
float
F
int
I
long
J
short
S
If this Class
object represents a primitive type or void
,
then the result is a string with the same spelling as the Java language
keyword which corresponds to the primitive type or void
.
Examples:
String.class.getName() returns "java.lang.String" byte.class.getName() returns "byte" (new Object[3]).getClass().getName() returns "[Ljava.lang.Object;" (new int[3][4][5][6][7][8][9]).getClass().getName() returns "[[[[[[[I"
Class
object.Object
. The returned
Class
object is the object that is locked by
static synchronized
methods of the represented class.
Object
. The returned
Class
object is the object that is locked by
static synchronized
methods of the represented class.
The actual result type is Class<? extends |X|>
where |X|
is the erasure of the static type of the
expression on which getClass
is called. For
example, no cast is required in this code fragment:
Number n = 0;
Class<? extends Number> c = n.getClass();
Class
object that represents the runtime
class of this object.The Template Method design pattern is a solution to implement a general class of algorithms, leaving some placeholders for the parts of the algorithm that vary from one context to another.
The pattern consists of implementing the common parts of the algorithm in a method (the template method) in an abstract class. This method will rely on abstract methods called primitives (the placeholders), which are implemented by subclasses.
Chapter 7, insight #14
Consider using the Template Method pattern in cases where an algorithm applies to all subclasses of a certain base class, except for some steps of the algorithm that must vary from subclass to subclass
The Template Method design pattern is a solution to implement a general class of algorithms, leaving some placeholders for the parts of the algorithm that vary from one context to another.
The pattern consists of implementing the common parts of the algorithm in a method (the template method) in an abstract class. This method will rely on abstract methods called primitives (the placeholders), which are implemented by subclasses.
Chapter 7, insight #14
Consider using the Template Method pattern in cases where an algorithm applies to all subclasses of a certain base class, except for some steps of the algorithm that must vary from subclass to subclass
AbstractMove
doesn't know what execute()
will do, but it knows how to
use the method as part of the perform()
implementation. This means that execute()
must be declared in this class (or a parent type) so that it can be called here.
But because the implementation isn't available yet, it must be abstract
.
Chapter 7, insight #13
Remember that abstract classes can define abstract methods, and that methods of the abstract class can call its own abstract methods. This way you can use abstract classes to define abstract implementations of algorithms
AbstractMove
doesn't know what execute()
will do, but it knows how to
use the method as part of the perform()
implementation. This means that execute()
must be declared in this class (or a parent type) so that it can be called here.
But because the implementation isn't available yet, it must be abstract
.
Chapter 7, insight #13
Remember that abstract classes can define abstract methods, and that methods of the abstract class can call its own abstract methods. This way you can use abstract classes to define abstract implementations of algorithms
Use of the final
keyword prevents any subclass from overridding
this method. Is thus ensures that any call to AbstractMove#execute
runs
this exact code.
Chapter 7, insight #15
If there is no scenario for overriding a method, consider declaring it final
. Similarly, if there is no specific reason for a class to be extensible using inheritance, consider declaring it final
.
Use of the final
keyword prevents any subclass from overridding
this method. Is thus ensures that any call to AbstractMove#execute
runs
this exact code.
Chapter 7, insight #15
If there is no scenario for overriding a method, consider declaring it final
. Similarly, if there is no specific reason for a class to be extensible using inheritance, consider declaring it final
.