Using design patterns with Java is a well-established topic. Design patterns also apply to Groovy:
-
some patterns carry over directly (and can make use of normal Groovy syntax improvements for greater readability)
-
some patterns are no longer required because they are built right into the language or because Groovy supports a better way of achieving the intent of the pattern
-
some patterns that have to be expressed at the design level in other languages can be implemented directly in Groovy (due to the way Groovy can blur the distinction between design and implementation)
1. Patterns
1.1. Abstract Factory Pattern
The Abstract Factory Pattern provides a way to encapsulate a group of individual factories that have a common theme. It embodies the intent of a normal factory, i.e. remove the need for code using an interface to know the concrete implementation behind the interface, but applies to a set of interfaces and selects an entire family of concrete classes which implement those interfaces.
As an example, I might have interfaces Button, TextField and Scrollbar. I might have WindowsButton, MacButton, FlashButton as concrete classes for Button. I might have WindowsScrollBar, MacScrollBar and FlashScrollBar as concrete implementations for ScrollBar. Using the Abstract Factory Pattern should allow me to select which windowing system (i.e. Windows, Mac, Flash) I want to use once and from then on should be able to write code that references the interfaces but is always using the appropriate concrete classes (all from the one windowing system) under the covers.
1.1.1. Example
Suppose we want to write a game system. We might note that many games have very similar features and control.
We decide to try to split the common and game-specific code into separate classes.
First let’s look at the game-specific code for a Two-up game:
class TwoupMessages {
def welcome = 'Welcome to the twoup game, you start with $1000'
def done = 'Sorry, you have no money left, goodbye'
}
class TwoupInputConverter {
def convert(input) { input.toInteger() }
}
class TwoupControl {
private money = 1000
private random = new Random()
private tossWasHead() {
def next = random.nextInt()
return next % 2 == 0
}
def moreTurns() {
if (money > 0) {
println "You have $money, how much would you like to bet?"
return true
}
false
}
def play(amount) {
def coin1 = tossWasHead()
def coin2 = tossWasHead()
if (coin1 && coin2) {
money += amount
println 'You win'
} else if (!coin1 && !coin2) {
money -= amount
println 'You lose'
} else {
println 'Draw'
}
}
}
Now, let’s look at the game-specific code for a number guessing game:
class GuessGameMessages {
def welcome = 'Welcome to the guessing game, my secret number is between 1 and 100'
def done = 'Correct'
}
class GuessGameInputConverter {
def convert(input) { input.toInteger() }
}
class GuessGameControl {
private lower = 1
private upper = 100
private guess = new Random().nextInt(upper - lower) + lower
def moreTurns() {
def done = (lower == guess || upper == guess)
if (!done) {
println "Enter a number between $lower and $upper"
}
!done
}
def play(nextGuess) {
if (nextGuess <= guess) {
lower = [lower, nextGuess].max()
}
if (nextGuess >= guess) {
upper = [upper, nextGuess].min()
}
}
}
Now, let’s write our factory code:
def guessFactory = [messages: GuessGameMessages, control: GuessGameControl, converter: GuessGameInputConverter]
def twoupFactory = [messages: TwoupMessages, control: TwoupControl, converter: TwoupInputConverter]
class GameFactory {
def static factory
def static getMessages() { return factory.messages.newInstance() }
def static getControl() { return factory.control.newInstance() }
def static getConverter() { return factory.converter.newInstance() }
}
The important aspect of this factory is that it allows selection of an entire family of concrete classes.
Here is how we would use the factory:
GameFactory.factory = twoupFactory
def messages = GameFactory.messages
def control = GameFactory.control
def converter = GameFactory.converter
println messages.welcome
def reader = new BufferedReader(new InputStreamReader(System.in))
while (control.moreTurns()) {
def input = reader.readLine().trim()
control.play(converter.convert(input))
}
println messages.done
Note that the first line configures which family of concrete game classes we will use. It’s not important that we selected which family to use by using the factory property as shown in the first line. Other ways would be equally valid examples of this pattern. For example, we may have asked the user which game they wanted to play or determined which game from an environment setting.
With the code as shown, the game might look like this when run:
Welcome to the twoup game, you start with $1000 You have 1000, how much would you like to bet? 300 Draw You have 1000, how much would you like to bet? 700 You win You have 1700, how much would you like to bet? 1700 You lose Sorry, you have no money left, goodbye
If we change the first line of the script to GameFactory.factory = guessFactory, then the sample run might look like this:
Welcome to the guessing game, my secret number is between 1 and 100 Enter a number between 1 and 100 75 Enter a number between 1 and 75 35 Enter a number between 1 and 35 15 Enter a number between 1 and 15 5 Enter a number between 5 and 15 10 Correct
1.2. Adapter Pattern
The Adapter Pattern (sometimes called the wrapper pattern) allows objects satisfying one interface to be used where another type of interface is expected. There are two typical flavours of the pattern: the delegation flavour and the inheritance flavour.
1.2.1. Delegation Example
Suppose we have the following classes:
class SquarePeg {
def width
}
class RoundPeg {
def radius
}
class RoundHole {
def radius
def pegFits(peg) {
peg.radius <= radius
}
String toString() { "RoundHole with radius $radius" }
}
We can ask the RoundHole
class if a RoundPeg
fits in it, but if we ask the same question for a SquarePeg
, then it will fail because the SquarePeg
class doesn’t have a radius
property (i.e. doesn’t satisfy the required interface).
To get around this problem, we can create an adapter to make it appear to have the correct interface. It would look like this:
class SquarePegAdapter {
def peg
def getRadius() {
Math.sqrt(((peg.width / 2) ** 2) * 2)
}
String toString() {
"SquarePegAdapter with peg width $peg.width (and notional radius $radius)"
}
}
We can use the adapter like this:
def hole = new RoundHole(radius: 4.0)
(4..7).each { w ->
def peg = new SquarePegAdapter(peg: new SquarePeg(width: w))
if (hole.pegFits(peg)) {
println "peg $peg fits in hole $hole"
} else {
println "peg $peg does not fit in hole $hole"
}
}
Which results in the following output:
peg SquarePegAdapter with peg width 4 (and notional radius 2.8284271247461903) fits in hole RoundHole with radius 4.0 peg SquarePegAdapter with peg width 5 (and notional radius 3.5355339059327378) fits in hole RoundHole with radius 4.0 peg SquarePegAdapter with peg width 6 (and notional radius 4.242640687119285) does not fit in hole RoundHole with radius 4.0 peg SquarePegAdapter with peg width 7 (and notional radius 4.949747468305833) does not fit in hole RoundHole with radius 4.0
1.2.2. Inheritance Example
Let’s consider the same example again using inheritance. First, here are the original classes (unchanged):
class SquarePeg {
def width
}
class RoundPeg {
def radius
}
class RoundHole {
def radius
def pegFits(peg) {
peg.radius <= radius
}
String toString() { "RoundHole with radius $radius" }
}
An adapter using inheritance:
class SquarePegAdapter extends SquarePeg {
def getRadius() {
Math.sqrt(((width / 2) ** 2) * 2)
}
String toString() {
"SquarePegAdapter with width $width (and notional radius $radius)"
}
}
Using the adapter:
def hole = new RoundHole(radius: 4.0)
(4..7).each { w ->
def peg = new SquarePegAdapter(width: w)
if (hole.pegFits(peg)) {
println "peg $peg fits in hole $hole"
} else {
println "peg $peg does not fit in hole $hole"
}
}
The output:
peg SquarePegAdapter with width 4 (and notional radius 2.8284271247461903) fits in hole RoundHole with radius 4.0 peg SquarePegAdapter with width 5 (and notional radius 3.5355339059327378) fits in hole RoundHole with radius 4.0 peg SquarePegAdapter with width 6 (and notional radius 4.242640687119285) does not fit in hole RoundHole with radius 4.0 peg SquarePegAdapter with width 7 (and notional radius 4.949747468305833) does not fit in hole RoundHole with radius 4.0
1.2.3. Adapting using Closures
As a variation of the previous examples, we could instead define the following interface:
interface RoundThing {
def getRadius()
}
We can then define an adapter as a closure as follows:
def adapter = {
p -> [getRadius: { Math.sqrt(((p.width / 2) ** 2) * 2) }] as RoundThing
}
And use it like this:
def peg = new SquarePeg(width: 4)
if (hole.pegFits(adapter(peg))) {
// ... as before
}
1.2.4. Adapting using the ExpandoMetaClass
As of Groovy 1.1, there is a built-in MetaClass which can automatically add properties and methods dynamically.
Here is how the example would work using that feature:
def peg = new SquarePeg(width: 4)
peg.metaClass.radius = Math.sqrt(((peg.width / 2) ** 2) * 2)
After you create a peg object, you can simply add a property to it on the fly. No need to change the original class and no need for an adapter class.
1.3. Bouncer Pattern
The Bouncer Pattern describes usage of a method whose sole purpose is to either throw an exception (when particular conditions hold) or do nothing. Such methods are often used to defensively guard pre-conditions of a method.
When writing utility methods, you should always guard against faulty input arguments. When writing internal methods, you may be able to ensure that certain pre-conditions always hold by having sufficient unit tests in place. Under such circumstances, you may reduce the desirability to have guards on your methods.
Groovy differs from other languages in that you frequently use the assert
method within your methods rather than having a large number of utility checker methods or classes.
1.3.1. Null Checking Example
We might have a utility method such as:
class NullChecker {
static check(name, arg) {
if (arg == null) {
throw new IllegalArgumentException(name + ' is null')
}
}
}
And we would use it like this:
void doStuff(String name, Object value) {
NullChecker.check('name', name)
NullChecker.check('value', value)
// do stuff
}
But a more Groovy way to do this would simply be like this:
void doStuff(String name, Object value) {
assert name != null, 'name should not be null'
assert value != null, 'value should not be null'
// do stuff
}
1.3.2. Validation Example
As an alternative example, we might have this utility method:
class NumberChecker {
static final String NUMBER_PATTERN = "\\\\d+(\\\\.\\\\d+(E-?\\\\d+)?)?"
static isNumber(str) {
if (!str ==~ NUMBER_PATTERN) {
throw new IllegalArgumentException("Argument '$str' must be a number")
}
}
static isNotZero(number) {
if (number == 0) {
throw new IllegalArgumentException('Argument must not be 0')
}
}
}
And we would use it like this:
def stringDivide(String dividendStr, String divisorStr) {
NumberChecker.isNumber(dividendStr)
NumberChecker.isNumber(divisorStr)
def dividend = dividendStr.toDouble()
def divisor = divisorStr.toDouble()
NumberChecker.isNotZero(divisor)
dividend / divisor
}
println stringDivide('1.2E2', '3.0')
// => 40.0
But with Groovy we could just as easily use:
def stringDivide(String dividendStr, String divisorStr) {
assert dividendStr =~ NumberChecker.NUMBER_PATTERN
assert divisorStr =~ NumberChecker.NUMBER_PATTERN
def dividend = dividendStr.toDouble()
def divisor = divisorStr.toDouble()
assert divisor != 0, 'Divisor must not be 0'
dividend / divisor
}
1.4. Chain of Responsibility Pattern
In the Chain of Responsibility Pattern, objects using and implementing an interface (one or more methods) are intentionally loosely coupled. A set of objects that implement the interface are organised in a list (or in rare cases a tree). Objects using the interface make requests from the first implementor object. It will decide whether to perform any action itself and whether to pass the request further down the line in the list (or tree). Sometimes a default implementation for some request is also coded into the pattern if none of the implementors respond to the request.
1.4.1. Example using traditional classes
In this example, the script sends requests to the lister
object. The lister
points to a UnixLister
object. If it can’t handle the request, it sends the request to the WindowsLister
. If it can’t handle the request, it sends the request to the DefaultLister
.
class UnixLister {
private nextInLine
UnixLister(next) { nextInLine = next }
def listFiles(dir) {
if (System.getProperty('os.name') == 'Linux') {
println "ls $dir".execute().text
} else {
nextInLine.listFiles(dir)
}
}
}
class WindowsLister {
private nextInLine
WindowsLister(next) { nextInLine = next }
def listFiles(dir) {
if (System.getProperty('os.name').startsWith('Windows')) {
println "cmd.exe /c dir $dir".execute().text
} else {
nextInLine.listFiles(dir)
}
}
}
class DefaultLister {
def listFiles(dir) {
new File(dir).eachFile { f -> println f }
}
}
def lister = new UnixLister(new WindowsLister(new DefaultLister()))
lister.listFiles('Downloads')
The output will be a list of files (with slightly different format depending on the operating system).
Here is a UML representation:
1.4.2. Example using simplifying strategies
For simple cases, consider simplifying your code by not requiring the chain of classes. Instead, use Groovy truth and the elvis operator as shown here:
String unixListFiles(dir) {
if (System.getProperty('os.name') == 'Linux') {
"ls $dir".execute().text
}
}
String windowsListFiles(dir) {
if (System.getProperty('os.name').startsWith('Windows')) {
"cmd.exe /c dir $dir".execute().text
}
}
String defaultListFiles(dir) {
new File(dir).listFiles().collect{ f -> f.name }.join('\\n')
}
def dir = 'Downloads'
println unixListFiles(dir) ?: windowsListFiles(dir) ?: defaultListFiles(dir)
Or Groovy’s switch as shown here:
String listFiles(dir) {
switch(dir) {
case { System.getProperty('os.name') == 'Linux' }:
return "ls $dir".execute().text
case { System.getProperty('os.name').startsWith('Windows') }:
return "cmd.exe /c dir $dir".execute().text
default:
new File(dir).listFiles().collect{ f -> f.name }.join('\\n')
}
}
println listFiles('Downloads')
Alternatively, for Groovy 3+, consider using streams of lambdas as shown here:
Optional<String> unixListFiles(String dir) {
Optional.ofNullable(dir)
.filter(d -> System.getProperty('os.name') == 'Linux')
.map(d -> "ls $d".execute().text)
}
Optional<String> windowsListFiles(String dir) {
Optional.ofNullable(dir)
.filter(d -> System.getProperty('os.name').startsWith('Windows'))
.map(d -> "cmd.exe /c dir $d".execute().text)
}
Optional<String> defaultListFiles(String dir) {
Optional.ofNullable(dir)
.map(d -> new File(d).listFiles().collect{ f -> f.name }.join('\\n'))
}
def dir = 'Downloads'
def handlers = [this::unixListFiles, this::windowsListFiles, this::defaultListFiles]
println handlers.stream()
.map(f -> f(dir))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst()
.get()
1.4.3. When not to use
If your use of chain of responsibility involves frequent use of the instanceof
operator, like here:
import static Math.PI as π
abstract class Shape {
String name
}
class Polygon extends Shape {
String name
double lengthSide
int numSides
}
class Circle extends Shape {
double radius
}
class CircleAreaCalculator {
def next
def area(shape) {
if (shape instanceof Circle) { (1)
return shape.radius ** 2 * π
} else {
next.area(shape)
}
}
}
class SquareAreaCalculator {
def next
def area(shape) {
if (shape instanceof Polygon && shape.numSides == 4) { (1)
return shape.lengthSide ** 2
} else {
next.area(shape)
}
}
}
class DefaultAreaCalculator {
def area(shape) {
throw new IllegalArgumentException("Don't know how to calculate area for $shape")
}
}
def chain = new CircleAreaCalculator(next: new SquareAreaCalculator(next: new DefaultAreaCalculator()))
def shapes = [
new Circle(name: 'Circle', radius: 5.0),
new Polygon(name: 'Square', lengthSide: 10.0, numSides: 4)
]
shapes.each { println chain.area(it) }
1 | instanceof code smell |
It could indicate that instead of using the chain of responsibility pattern, you might consider using richer types, perhaps in combination with Groovy’s multimethods. For example, perhaps this:
// ...
class Square extends Polygon {
// ...
}
double area(Circle c) {
c.radius ** 2 * π
}
double area(Square s) {
s.lengthSide ** 2
}
def shapes = [
new Circle(radius: 5.0),
new Square(lengthSide: 10.0, numSides: 4)
]
shapes.each { println area(it) }
or using more traditional object-oriented style like this:
import static Math.PI as π
interface Shape {
double area()
}
abstract class Polygon implements Shape {
double lengthSide
int numSides
abstract double area()
}
class Circle implements Shape {
double radius
double area() {
radius ** 2 * π
}
}
class Square extends Polygon {
// ...
double area() {
lengthSide ** 2
}
}
def shapes = [
new Circle(radius: 5.0),
new Square(lengthSide: 10.0, numSides: 4)
]
shapes.each { println it.area() }
1.4.4. Going further
Other variations to this pattern:
-
we could have an explicit interface in the traditional example, e.g.
Lister
, to statically type the implementations but because of duck-typing this is optional -
we could use a chain tree instead of a list, e.g.
if (animal.hasBackbone())
delegate toVertebrateHandler
else delegate toInvertebrateHandler
-
we could always pass down the chain even if we processed a request (no early return)
-
we could decide at some point to not respond and not pass down the chain (pre-emptive abort)
-
we could use Groovy’s meta-programming capabilities to pass unknown methods down the chain, e.g. combine chain of responsibility with the use of
methodMissing
1.5. Command Pattern
The Command Pattern is a pattern for loosely coupling a client object which wants to execute a series of commands and receiver objects which enact those commands. Instead of talking to receivers directly, clients interact with an intermediary object which then relays the necessary commands to the receivers. The pattern is in common use within the JDK, for example the api:javax.swing.Action[] class in Swing decouples swing code from receivers like buttons, menu items and panels.
The class diagram showing the typical classes is:
The sequence of interactions is as shown below for an arbitrary receiver:
1.5.1. Example with traditional classes
The relevant classes required for turning a light on and off (see the example in the earlier wikipedia reference) would be as follows:
interface Command {
void execute()
}
// invoker class
class Switch {
private final Map<String, Command> commandMap = new HashMap<>()
void register(String commandName, Command command) {
commandMap[commandName] = command
}
void execute(String commandName) {
Command command = commandMap[commandName]
if (!command) {
throw new IllegalStateException("no command registered for " + commandName)
}
command.execute()
}
}
// receiver class
class Light {
void turnOn() {
println "The light is on"
}
void turnOff() {
println "The light is off"
}
}
class SwitchOnCommand implements Command {
Light light
@Override // Command
void execute() {
light.turnOn()
}
}
class SwitchOffCommand implements Command {
Light light
@Override // Command
void execute() {
light.turnOff()
}
}
Light lamp = new Light()
Command switchOn = new SwitchOnCommand(light: lamp)
Command switchOff = new SwitchOffCommand(light: lamp)
Switch mySwitch = new Switch()
mySwitch.register("on", switchOn)
mySwitch.register("off", switchOff)
mySwitch.execute("on")
mySwitch.execute("off")
Our client scripts sends execute
commands to an intermediary and knows nothing
about any specific receivers, or any specific action method names and arguments.
1.5.2. Simplifying variations
Given that Groovy has first-class function support, we can do away with the
actual command classes (like SwitchOnCommand
) by instead using closures as shown here:
interface Command {
void execute()
}
// invoker class
class Switch {
private final Map<String, Command> commandMap = [:]
void register(String commandName, Command command) {
commandMap[commandName] = command
}
void execute(String commandName) {
Command command = commandMap[commandName]
if (!command) {
throw new IllegalStateException("no command registered for $commandName")
}
command.execute()
}
}
// receiver class
class Light {
void turnOn() {
println 'The light is on'
}
void turnOff() {
println 'The light is off'
}
}
Light lamp = new Light()
Switch mySwitch = new Switch()
mySwitch.register("on", lamp.&turnOn) (1)
mySwitch.register("off", lamp.&turnOff) (1)
mySwitch.execute("on")
mySwitch.execute("off")
1 | Command closures (here method closures) but could be lambdas/method references for Groovy 3+ |
We can simplify further using the JDK’s existing Runnable
interface
and using a switch map rather than a separate Switch
class as shown here:
class Light {
void turnOn() {
println 'The light is on'
}
void turnOff() {
println 'The light is off'
}
}
class Door {
static void unlock() {
println 'The door is unlocked'
}
}
Light lamp = new Light()
Map<String, Runnable> mySwitch = [
on: lamp::turnOn,
off: lamp::turnOff,
unlock: Door::unlock
]
mySwitch.on()
mySwitch.off()
mySwitch.unlock()
We have added an additional Door
receiver to illustrate how to expand the original example.
Running this script results in:
The light is on The light is off The door is unlocked
As a variation, if the command names aren’t important to us, we can forgo using the switch map and just have a list of tasks to invoke as shown here:
// ...
List<Runnable> tasks = [lamp::turnOn, lamp::turnOff, Door::unlock]
tasks.each{ it.run() }
1.6. Composite Pattern
The Composite Pattern allows you to treat single instances of an object the same way as a group of objects. The pattern is often used with hierarchies of objects. Typically, one or more methods should be callable in the same way for either leaf or composite nodes within the hierarchy. In such a case, composite nodes typically invoke the same named method for each of their children nodes.
1.6.1. Example
Consider this usage of the composite pattern where we want to call toString()
on either Leaf
or Composite
objects.
In Java, the Component
class is essential as it provides the type used for both leaf and composite nodes. In Groovy, because of duck-typing, we don’t need it for that purpose, however, it can still serve as a useful place to place common behaviour between the leaf and composite nodes.
For our purposes, we will assemble the following hierarchy of components.
Here is the code:
abstract class Component {
def name
def toString(indent) {
("-" * indent) + name
}
}
class Composite extends Component {
private children = []
def toString(indent) {
def s = super.toString(indent)
children.each { child ->
s += "\\n" + child.toString(indent + 1)
}
s
}
def leftShift(component) {
children << component
}
}
class Leaf extends Component { }
def root = new Composite(name: "root")
root << new Leaf(name: "leaf A")
def comp = new Composite(name: "comp B")
root << comp
root << new Leaf(name: "leaf C")
comp << new Leaf(name: "leaf B1")
comp << new Leaf(name: "leaf B2")
println root.toString(0)
Here is the resulting output:
root -leaf A -comp B --leaf B1 --leaf B2 -leaf C
1.7. Decorator Pattern
The Decorator Pattern provides a mechanism to embellish the behaviour of an object without changing its essential interface. A decorated object should be able to be substituted wherever the original (non-decorated) object was expected. Decoration typically does not involve modifying the source code of the original object and decorators should be able to be combined in flexible ways to produce objects with several embellishments.
1.7.1. Traditional Example
Suppose we have the following Logger
class.
class Logger {
def log(String message) {
println message
}
}
There might be times when it is useful to timestamp a log message, or times when we might want to change the case of the message. We could try to build all of this functionality into our Logger
class. If we did that, the Logger
class would start to be very complex. Also, everyone would obtain all of the features even when they might want only a small subset of the features. Finally, feature interaction would become quite difficult to control.
To overcome these drawbacks, we instead define two decorator classes. Uses of the Logger
class are free to embellish their base logger with zero or more decorator classes in whatever order they desire. The classes look like this:
class TimeStampingLogger extends Logger {
private Logger logger
TimeStampingLogger(logger) {
this.logger = logger
}
def log(String message) {
def now = Calendar.instance
logger.log("$now.time: $message")
}
}
class UpperLogger extends Logger {
private Logger logger
UpperLogger(logger) {
this.logger = logger
}
def log(String message) {
logger.log(message.toUpperCase())
}
}
We can use the decorators like so:
def logger = new UpperLogger(new TimeStampingLogger(new Logger()))
logger.log("G'day Mate")
// => Tue May 22 07:13:50 EST 2007: G'DAY MATE
You can see that we embellish the logger behaviour with both decorators. Because of the order we chose to apply the decorators, our log message comes out capitalised and the timestamp is in normal case. If we swap the order around, let’s see what happens:
logger = new TimeStampingLogger(new UpperLogger(new Logger()))
logger.log('Hi There')
// => TUE MAY 22 07:13:50 EST 2007: HI THERE
Now the timestamp itself has also been changed to be uppercase.
1.7.2. Simplifying with closures or lambdas
Closures make it easy to represent code. We can use that fact to make a general purpose logger class that accepts the decoration code as a closure. This saves us defining many decoration classes.
class DecoratingLogger {
def decoration = Closure.IDENTITY
def log(String message) {
println decoration(message)
}
}
def upper = { it.toUpperCase() }
def stamp = { "$Calendar.instance.time: $it" }
def logger = new DecoratingLogger(decoration: stamp << upper)
logger.log("G'day Mate")
// Sat Aug 29 15:28:29 AEST 2020: G'DAY MATE
We can use the same approach with lambdas:
import java.util.function.Function
class DecoratingLogger {
Function<String, String> decoration = Function.identity()
def log(String message) {
println decoration.apply(message)
}
}
Function<String, String> upper = s -> s.toUpperCase()
Function<String, String> stamp = s -> "$Calendar.instance.time: $s"
def logger = new DecoratingLogger(decoration: upper.andThen(stamp))
logger.log("G'day Mate")
// => Sat Aug 29 15:38:28 AEST 2020: G'DAY MATE
1.7.3. A touch of dynamic behaviour
Our previous decorators were specific to Logger
objects. We can use Groovy’s Meta-Object Programming capabilities to create a decorator which is far more general purpose in nature. Consider this class:
class GenericLowerDecorator {
private delegate
GenericLowerDecorator(delegate) {
this.delegate = delegate
}
def invokeMethod(String name, args) {
def newargs = args.collect { arg ->
if (arg instanceof String) {
return arg.toLowerCase()
} else {
return arg
}
}
delegate.invokeMethod(name, newargs)
}
}
It takes any class and decorates it so that any String
method parameter will automatically be changed to lower case.
logger = new GenericLowerDecorator(new TimeStampingLogger(new Logger()))
logger.log('IMPORTANT Message')
// => Tue May 22 07:27:18 EST 2007: important message
Just be careful with ordering here. The original decorators were restricted to decorating Logger
objects. This decorator works with any object type, so we can’t swap the ordering around, i.e. this won’t work:
// Can't mix and match Interface-Oriented and Generic decorators // logger = new TimeStampingLogger(new GenericLowerDecorator(new Logger()))
We could overcome this limitation be generating an appropriate Proxy type at runtime but we won’t complicate the example here.
1.7.4. Runtime behaviour embellishment
You can also consider using the ExpandoMetaClass
from Groovy 1.1 to dynamically embellish a class with behaviour. This isn’t the normal style of usage of the decorator pattern (it certainly isn’t nearly as flexible) but may help you to achieve similar results in some cases without creating a new class.
Here’s what the code looks like:
// current mechanism to enable ExpandoMetaClass
GroovySystem.metaClassRegistry.metaClassCreationHandle = new ExpandoMetaClassCreationHandle()
def logger = new Logger()
logger.metaClass.log = { String m -> println 'message: ' + m.toUpperCase() }
logger.log('x')
// => message: X
This achieves a similar result to applying a single decorator but we have no way to easily apply and remove embellishments on the fly.
1.7.5. More dynamic decorating
Suppose we have a calculator class (Actually any class would do).
class Calc {
def add(a, b) { a + b }
}
We might be interested in observing usage of the class over time. If it is buried deep within our codebase, it might be hard to determine when it is being called and with what parameters. Also, it might be hard to know if it is performing well. We can easily make a generic tracing decorator that prints out tracing information whenever any method on the Calc
class is called and also provide timing information about how long it took to execute. Here is the code for the tracing decorator:
class TracingDecorator {
private delegate
TracingDecorator(delegate) {
this.delegate = delegate
}
def invokeMethod(String name, args) {
println "Calling $name$args"
def before = System.currentTimeMillis()
def result = delegate.invokeMethod(name, args)
println "Got $result in ${System.currentTimeMillis()-before} ms"
result
}
}
Here is how to use the class in a script:
def tracedCalc = new TracingDecorator(new Calc())
assert 15 == tracedCalc.add(3, 12)
And here is what you would see after running this script:
Calling add{3, 12} Got 15 in 31 ms
1.7.6. Decorating with an Interceptor
The above timing example hooks into the lifecycle of Groovy objects (via invokeMethod
). This is such an important style performing meta-programming that Groovy has special support for this style of decorating using interceptors.
Groovy even comes with a built-in TracingInterceptor
. We can extend the built-in class like this:
class TimingInterceptor extends TracingInterceptor {
private beforeTime
def beforeInvoke(object, String methodName, Object[] arguments) {
super.beforeInvoke(object, methodName, arguments)
beforeTime = System.currentTimeMillis()
}
Object afterInvoke(Object object, String methodName, Object[] arguments, Object result) {
super.afterInvoke(object, methodName, arguments, result)
def duration = System.currentTimeMillis() - beforeTime
writer.write("Duration: $duration ms\\n")
writer.flush()
result
}
}
Here is an example of using this new class:
def proxy = ProxyMetaClass.getInstance(Calc)
proxy.interceptor = new TimingInterceptor()
proxy.use {
assert 7 == new Calc().add(1, 6)
}
And here is the output:
before Calc.ctor() after Calc.ctor() Duration: 0 ms before Calc.add(java.lang.Integer, java.lang.Integer) after Calc.add(java.lang.Integer, java.lang.Integer) Duration: 2 ms
1.7.7. Decorating with java.lang.reflect.Proxy
If you are trying to decorate an object (i.e. just a particular instance of the class, not the class generally), then you can use Java’s java.lang.reflect.Proxy
. Groovy makes working with this easier than just Java. Below is a code sample taken out of a grails project that wraps a java.sql.Connection
so that it’s close method is a no-op:
protected Sql getGroovySql() {
final Connection con = session.connection()
def invoker = { object, method, args ->
if (method.name == "close") {
log.debug("ignoring call to Connection.close() for use by groovy.sql.Sql")
} else {
log.trace("delegating $method")
return con.invokeMethod(method.name, args)
}
} as InvocationHandler;
def proxy = Proxy.newProxyInstance( getClass().getClassLoader(), [Connection] as Class[], invoker )
return new Sql(proxy)
}
If there were many methods to intercept, then this approach could be modified to look up closure in a map by method name and invoke it.
1.7.8. Decorating with Spring
The Spring Framework allows decorators to be applied with interceptors (you may have heard the terms advice or aspect). You can leverage this mechanism from Groovy as well.
First define a class that you want to decorate (we’ll also use an interface as is normal Spring practice):
Here’s the interface:
interface Calc {
def add(a, b)
}
Here’s the class:
class CalcImpl implements Calc {
def add(a, b) { a + b }
}
Now, we define our wiring in a file called beans.xml
as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang https://www.springframework.org/schema/lang/spring-lang.xsd">
<bean id="performanceInterceptor" autowire="no"
class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor">
<property name="loggerName" value="performance"/>
</bean>
<bean id="calc" class="util.CalcImpl"/>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="calc"/>
<property name="interceptorNames" value="performanceInterceptor"/>
</bean>
</beans>
Now, our script looks like this:
@Grab('org.springframework:spring-context:5.2.8.RELEASE')
import org.springframework.context.support.ClassPathXmlApplicationContext
def ctx = new ClassPathXmlApplicationContext('beans.xml')
def calc = ctx.getBean('calc')
println calc.add(3, 25)
And when we run it, we see the results:
21/05/2007 23:02:35 org.springframework.aop.interceptor.PerformanceMonitorInterceptor invokeUnderTrace FINEST: StopWatch 'util.Calc.add': running time (millis) = 16
You may have to adjust your logging.properties
file for messages at log level FINEST
to be displayed.
1.7.9. Asynchronous Decorators using GPars
The following example is inspired by some of the early example code for the Panini programming language. These days, you’ll see this style used with async functions in JavaScript.
@Grab('org.codehaus.gpars:gpars:0.10')
import static groovyx.gpars.GParsPool.withPool
interface Document {
void print()
String getText()
}
class DocumentImpl implements Document {
def document
void print() { println document }
String getText() { document }
}
def words(String text) {
text.replaceAll('[^a-zA-Z]', ' ').trim().split("\\\\s+")*.toLowerCase()
}
def avgWordLength = {
def words = words(it.text)
sprintf "Avg Word Length: %4.2f", words*.size().sum() / words.size()
}
def modeWord = {
def wordGroups = words(it.text).groupBy {it}.collectEntries { k, v -> [k, v.size()] }
def maxSize = wordGroups*.value.max()
def maxWords = wordGroups.findAll { it.value == maxSize }
"Mode Word(s): ${maxWords*.key.join(', ')} ($maxSize occurrences)"
}
def wordCount = { d -> "Word Count: " + words(d.text).size() }
def asyncDecorator(Document d, Closure c) {
ProxyGenerator.INSTANCE.instantiateDelegate([print: {
withPool {
def result = c.callAsync(d)
d.print()
println result.get()
}
}], [Document], d)
}
Document d = asyncDecorator(asyncDecorator(asyncDecorator(
new DocumentImpl(document:"This is the file with the words in it\\n\\t\\nDo you see the words?\\n"),
// new DocumentImpl(document: new File('AsyncDecorator.groovy').text),
wordCount), modeWord), avgWordLength)
d.print()
1.8. Delegation Pattern
The Delegation Pattern is a technique where an object’s behavior (public methods) is implemented by delegating responsibility to one or more associated objects.
Groovy allows the traditional style of applying the delegation pattern, e.g. see Replace Inheritance with Delegation.
1.8.1. Implement Delegation Pattern using ExpandoMetaClass
The groovy.lang.ExpandoMetaClass allows usage of this pattern to be encapsulated in a library. This allows Groovy to emulate similar libraries available for the Ruby language.
Consider the following library class:
class Delegator {
private targetClass
private delegate
Delegator(targetClass, delegate) {
this.targetClass = targetClass
this.delegate = delegate
}
def delegate(String methodName) {
delegate(methodName, methodName)
}
def delegate(String methodName, String asMethodName) {
targetClass.metaClass."$asMethodName" = delegate.&"$methodName"
}
def delegateAll(String[] names) {
names.each { delegate(it) }
}
def delegateAll(Map names) {
names.each { k, v -> delegate(k, v) }
}
def delegateAll() {
delegate.class.methods*.name.each { delegate(it) }
}
}
With this in your classpath, you can now apply the delegation pattern dynamically as shown in the following examples. First, consider we have the following classes:
class Person {
String name
}
class MortgageLender {
def borrowAmount(amount) {
"borrow \\$$amount"
}
def borrowFor(thing) {
"buy \\$thing"
}
}
def lender = new MortgageLender()
def delegator = new Delegator(Person, lender)
We can now use the delegator to automatically borrow methods from the lender object to extend the Person class. We can borrow the methods as is or with a rename:
delegator.delegate 'borrowFor'
delegator.delegate 'borrowAmount', 'getMoney'
def p = new Person()
println p.borrowFor('present') // => buy present
println p.getMoney(50)
The first line above, adds the borrowFor method to the Person class by delegating to the lender object. The second line adds a getMoney method to the Person class by delegating to the lender object’s borrowAmount method.
Alternatively, we could borrow multiple methods like this:
delegator.delegateAll 'borrowFor', 'borrowAmount'
Which adds these two methods to the Person class.
Or if we want all the methods, like this:
delegator.delegateAll()
Which will make all the methods in the delegate object available in the Person class.
Alternatively, we can use a map notation to rename multiple methods:
delegator.delegateAll borrowAmount:'getMoney', borrowFor:'getThing'
1.8.2. Implement Delegation Pattern using @Delegate annotation
Since version 1.6 you can use the built-in delegation mechanism which is based on AST transformation.
This make delegation even easier:
class Person {
def name
@Delegate MortgageLender mortgageLender = new MortgageLender()
}
class MortgageLender {
def borrowAmount(amount) {
"borrow \\$$amount"
}
def borrowFor(thing) {
"buy $thing"
}
}
def p = new Person()
assert "buy present" == p.borrowFor('present')
assert "borrow \\$50" == p.borrowAmount(50)
1.9. Flyweight Pattern
The Flyweight Pattern is a pattern for greatly reducing memory requirements by not requiring that heavy-weight objects be created in large numbers when dealing with systems that contain many things that are mostly the same. If for instance, a document was modelled using a complex character class that knew about unicode, fonts, positioning, etc., then the memory requirements could be quite large for large documents if each physical character in the document required its own character class instance. Instead, characters themselves might be kept within Strings and we might have one character class (or a small number such as one character class for each font type) that knew the specifics of how to deal with characters.
In such circumstances, we call the state that is shared with many other things (e.g. the character type) intrinsic state. It is captured within the heavy-weight class. The state which distinguishes the physical character (maybe just its ASCII code or Unicode) is called its extrinsic state.
1.9.1. Example
First we are going to model some complex aircraft (the first being a hoax competitor of the second - though that is not relevant to the example).
class Boeing797 {
def wingspan = '80.8 m'
def capacity = 1000
def speed = '1046 km/h'
def range = '14400 km'
// ...
}
class Airbus380 {
def wingspan = '79.8 m'
def capacity = 555
def speed = '912 km/h'
def range = '10370 km'
// ...
}
If we want to model our fleet, our first attempt might involve using many instances of these heavy-weight objects. It turns out though that only a few small pieces of state (our extrinsic state) change for each aircraft, so we will have singletons for the heavy-weight objects and capture the extrinsic state (bought date and asset number in the code below) separately.
class FlyweightFactory {
static instances = [797: new Boeing797(), 380: new Airbus380()]
}
class Aircraft {
private type // intrinsic state
private assetNumber // extrinsic state
private bought // extrinsic state
Aircraft(typeCode, assetNumber, bought) {
type = FlyweightFactory.instances[typeCode]
this.assetNumber = assetNumber
this.bought = bought
}
def describe() {
println """
Asset Number: $assetNumber
Capacity: $type.capacity people
Speed: $type.speed
Range: $type.range
Bought: $bought
"""
}
}
def fleet = [
new Aircraft(380, 1001, '10-May-2007'),
new Aircraft(380, 1002, '10-Nov-2007'),
new Aircraft(797, 1003, '10-May-2008'),
new Aircraft(797, 1004, '10-Nov-2008')
]
fleet.each { p -> p.describe() }
So here, even if our fleet contained hundreds of planes, we would only have one heavy-weight object for each type of aircraft.
As a further efficiency measure, we might use lazy creation of the flyweight objects rather than create the initial map up front as in the above example.
Running this script results in:
Asset Number: 1001 Capacity: 555 people Speed: 912 km/h Range: 10370 km Bought: 10-May-2007 Asset Number: 1002 Capacity: 555 people Speed: 912 km/h Range: 10370 km Bought: 10-Nov-2007 Asset Number: 1003 Capacity: 1000 people Speed: 1046 km/h Range: 14400 km Bought: 10-May-2008 Asset Number: 1004 Capacity: 1000 people Speed: 1046 km/h Range: 14400 km Bought: 10-Nov-2008
1.10. Iterator Pattern
The Iterator Pattern allows sequential access to the elements of an aggregate object without exposing its underlying representation.
Groovy has the iterator pattern built right in to many of its closure operators, e.g. each
and eachWithIndex
as well as the for .. in
loop.
For example:
def printAll(container) {
for (item in container) { println item }
}
def numbers = [ 1,2,3,4 ]
def months = [ Mar:31, Apr:30, May:31 ]
def colors = [ java.awt.Color.BLACK, java.awt.Color.WHITE ]
printAll numbers
printAll months
printAll colors
Results in the output:
1 2 3 4 May=31 Mar=31 Apr=30 java.awt.Color[r=0,g=0,b=0] java.awt.Color[r=255,g=255,b=255]
Another example:
colors.eachWithIndex { item, pos ->
println "Position $pos contains '$item'"
}
Results in:
Position 0 contains 'java.awt.Color[r=0,g=0,b=0]' Position 1 contains 'java.awt.Color[r=255,g=255,b=255]'
The iterator pattern is also built in to other special operators such as the eachByte
, eachFile
, eachDir
, eachLine
, eachObject
, eachMatch
operators for working with streams, URLs, files, directories and regular expressions matches.
1.11. Loan my Resource Pattern
The Loan my Resource pattern ensures that a resource is deterministically disposed of once it goes out of scope.
This pattern is built in to many Groovy helper methods. You should consider using it yourself if you need to work with resources in ways beyond what Groovy supports.
1.11.1. Example
Consider the following code which works with a file. First we might write some line to the file and then print its size:
def f = new File('junk.txt')
f.withPrintWriter { pw ->
pw.println(new Date())
pw.println(this.class.name)
}
println f.size()
// => 42
We could also read back the contents of the file a line at a time and print each line out:
f.eachLine { line ->
println line
}
// =>
// Mon Jun 18 22:38:17 EST 2007
// RunPattern
Note that normal Java Reader
and PrintWriter
objects were used under the covers by Groovy but the code writer did not have to worry about explicitly creating or closing those resources. The built-in Groovy methods loan the respective reader or writer to the closure code and then tidy up after themselves. So, you are using this pattern without having to do any work.
Sometimes however, you wish to do things slightly differently to what you can get for free using Groovy’s built-in mechanisms. You should consider utilising this pattern within your own resource-handling operations.
Consider how you might process the list of words on each line within the file. We could actually do this one too using Groovy’s built-in functions, but bear with us and assume we have to do some resource handling ourselves. Here is how we might write the code without using this pattern:
def reader = f.newReader()
reader.splitEachLine(' ') { wordList ->
println wordList
}
reader.close()
// =>
// [ "Mon", "Jun", "18", "22:38:17", "EST", "2007" ]
// [ "RunPattern" ]
Notice that we now have an explicit call to close()
in our code. If we didn’t code it just right (here we didn’t surround the code in a try … finally
block, we run the risk of leaving the file handle open.
Let’s now apply the loan pattern. First, we’ll write a helper method:
def withListOfWordsForEachLine(File f, Closure c) {
def r = f.newReader()
try {
r.splitEachLine(' ', c)
} finally {
r?.close()
}
}
Now, we can re-write our code as follows:
withListOfWordsForEachLine(f) { wordList ->
println wordList
}
// =>
// [ "Mon", "Jun", "18", "22:38:17", "EST", "2007" ]
// [ "RunPattern" ]
This is much simpler and has removed the explicit close()
. This is now catered for in one spot so we can apply the appropriate level of testing or reviewing in just one spot to be sure we have no problems.
1.12. Using Monoids
Monoids allow the mechanics of an aggregation algorithm to be separated from the algorithm-specific logic associated with that aggregation. It is often thought to be a functional design pattern.
Perhaps, it is easiest seen with an example. Consider the code for integer sum, integer product and string concatenation. We might note various similarities:
def nums = [1, 2, 3, 4]
def sum = 0 (1)
for (num in nums) { sum += num } (2)
assert sum == 10
def product = 1 (1)
for (num in nums) { product *= num } (2)
assert product == 24
def letters = ['a', 'b', 'c']
def concat = '' (1)
for (letter in letters) { concat += letter } (2)
assert concat == 'abc'
1 | Initialize an aggregate counter |
2 | Loop throw elements with for/while/iteration adjusting counter |
We can remove the duplicate aggregation coding and the tease out the important differences for each algorithm.
We might instead use Groovy’s inject
method. This is a fold operation in functional programming jargon.
assert nums.inject(0){ total, next -> total + next } == 10
assert nums.inject(1){ total, next -> total * next } == 24
assert letters.inject(''){ total, next -> total + next } == 'abc'
Here the first parameter is the initial value, and the supplied closure contains the algorithm-specific logic.
Similarly, for Groovy 3+, we can use the JDK stream API and lambda syntax as follows:
assert nums.stream().reduce(0, (total, next) -> total + next) == 10
assert nums.stream().reduce(1, (total, next) -> total * next) == 24
assert letters.stream().reduce('', (total, next) -> total + next) == 'abc'
1.12.1. A touch of formalism
Looking at these examples, we might think all aggregation can be supported this way. In fact, we look for certain characteristics to ensure that this aggregation pattern will apply:
-
Closure: performing the aggregation step should produce a result of the same type as the elements being aggregated.
When using the term closure here, we simply mean closed under the operation, not the Groovy |
-
Associativity: the order in which we apply the aggregation step should not matter.
-
Identity element (sometimes also called a 'zero' element): there should be an element which aggregated with any element returns the original element.
If your algorithm doesn’t satisfy all the monoid properties, that doesn’t mean aggregation isn’t possible. It just means that you won’t get all the benefits from monoids, which we’ll cover shortly, or you might have a little more work to do. Also, you might be able to convert your data structures slightly to turn your problem into one involving monoids. We’ll cover that topic a little later in this section.
1.12.2. Benefits of monoids
Consider adding the integers 10 through 16.
Because the operation of addition for integers is a monoid, we already know that we can save writing code
and instead use the approach we saw in the earlier inject
examples.
There are some other nice properties.
Because of the closure property,
if we have a pairwise method like sum(Integer a, Integer b)
, then for a monoid, we can always
extend that method to work with a list, e.g. sum(List<Integer> nums)
or sum(Integer first, Integer… rest)
.
Because of associativity, we can employ some interesting ways to solve the aggregation including:
-
Divide and conquer algorithms which break the problem into smaller pieces
-
Various incremental algorithms for example memoization would allow summing from 1..5 to potentially start part way through be reusing a cached value of summing 1..4 if that had been calculated earlier
-
Inherent parallelization can make use of multiple cores
Let’s just look at the first of these in more detail. With a multicore
processor, one core could add 10
plus 11
, another core 12
plus 13
, and so on.
We’d use the identity element if needed (shown being added to 16
in our example).
Then the intermediate results could also be added together concurrently and so on until the result was reached.
We have reduced the amount of code we need to write, and we also have potential performance gains.
Here is how we might code the previous example using the GPars concurrency and parallelism framework (two alternatives shown):
def nums = 10..16
GParsPool.withPool {
assert 91 == nums.injectParallel(0){ total, next -> total + next }
assert 91 == nums.parallel.reduce(0, (total, next) -> total + next)
}
1.12.3. Working with Non-monoids
Suppose we want to find the average of the numbers 1..10. Groovy has a built-in method for this:
assert (1..10).average() == 5.5
Now, suppose we want to build our own monoid solution instead of using the built-in version. It might seem difficult to find the identity element. After all:
assert (0..10).average() == 5
Similarly, if we are tempted to write the pairwise aggregation closure it might be something like:
def avg = { a, b -> (a + b) / 2 }
What b
can we use for the identity element here so that our equation returns the original?
We need to use a
, but that isn’t a fixed value, so there is no identity.
Also, associativity doesn’t hold for this initial attempt at defining avg
as these examples show:
assert 6 == avg(avg(10, 2), 6)
assert 7 == avg(10, avg(2, 6))
Also, what about our closure property? Our original numbers were integers, but our average (5.5
)
is not. We can solve this by making our average work for any Number
instances, but it might not always be this easy.
It might appear that this problem is not amenable to a monoidal solution. However, there are numerous ways to bring monoids into the solution.
We can split it into two parts:
def nums = 1..10
def total = nums.sum()
def avg = total / nums.size()
assert avg == 5.5
The calculation of sum()
can follow monoid rules and then our last step can calculate the average.
We can even do a concurrent version with GPars:
withPool {
assert 5.5 == nums.sumParallel() / nums.size()
}
Here, we were using the built-in sum()
method (and sumParallel()
for the GPars example),
but if you were doing it by hand, the monoid nature of that part of your calculation would
make it easier to write your own code for that step.
Alternatively, we can introduce a helper data structure that reworks the problem to be a monoid. Instead of just keeping the total, let’s keep a list containing the total and count of numbers. The code could look something like this:
def holder = nums
.collect{ [it, 1] }
.inject{ a, b -> [a[0] + b[0], a[1] + b[1]] }
def avg = holder[0] / holder[1]
assert avg == 5.5
Or, to be a little fancier, we could introduce a class for our data structure and even calculate concurrently:
class AverageHolder {
int total
int count
AverageHolder plus(AverageHolder other) {
return new AverageHolder(total: total + other.total,
count: count + other.count)
}
static final AverageHolder ZERO =
new AverageHolder(total: 0, count: 0)
}
def asHolder = {
it instanceof Integer ? new AverageHolder(total: it, count : 1) : it
}
def pairwiseAggregate = { aggregate, next ->
asHolder(aggregate) + asHolder(next)
}
withPool {
def holder = nums.injectParallel(AverageHolder.ZERO, pairwiseAggregate)
def avg = holder.with{ total / count }
assert avg == 5.5
}
1.13. Null Object Pattern
The Null Object Pattern involves using a special object place-marker object representing null. Typically, if you have a reference to null, you can’t invoke reference.field
or reference.method()
You receive the dreaded NullPointerException
. The null object pattern uses a special object representing null, instead of using an actual null
. This allows you to invoke field and method references on the null object. The result of using the null object should semantically be equivalent to doing nothing.
1.13.1. Simple Example
Suppose we have the following system:
class Job {
def salary
}
class Person {
def name
def Job job
}
def people = [
new Person(name: 'Tom', job: new Job(salary: 1000)),
new Person(name: 'Dick', job: new Job(salary: 1200)),
]
def biggestSalary = people.collect { p -> p.job.salary }.max()
println biggestSalary
When run, this prints out 1200
. Suppose now that we now invoke:
people << new Person(name: 'Harry')
If we now try to calculate biggestSalary
again, we receive a null pointer exception.
To overcome this problem, we can introduce a NullJob
class and change the above statement to become:
class NullJob extends Job { def salary = 0 }
people << new Person(name: 'Harry', job: new NullJob())
biggestSalary = people.collect { p -> p.job.salary }.max()
println biggestSalary
This works as we require but it’s not always the best way to do this with Groovy. Groovy’s safe-dereference operator (?.
) operator and null aware closures often allow Groovy to avoid the need to create a special null object or null class. This is illustrated by examining a groovier way to write the above example:
people << new Person(name:'Harry')
biggestSalary = people.collect { p -> p.job?.salary }.max()
println biggestSalary
Two things are going on here to allow this to work. First of all, max()
is 'null aware' so that [300, null, 400].max() == 400. Secondly, with the ?.
operator, an expression like p?.job?.salary
will be equal to null if salary
is equal to null, or if job
is equal ` null or if p
is equal to null. You don’t need to code a complex nested if ... then ... else to avoid a NullPointerException
.
1.13.2. Tree Example
Consider the following example where we want to calculate size, cumulative sum and cumulative product of all the values in a tree structure.
Our first attempt has special logic within the calculation methods to handle null values.
class NullHandlingTree {
def left, right, value
def size() {
1 + (left ? left.size() : 0) + (right ? right.size() : 0)
}
def sum() {
value + (left ? left.sum() : 0) + (right ? right.sum() : 0)
}
def product() {
value * (left ? left.product() : 1) * (right ? right.product() : 1)
}
}
def root = new NullHandlingTree(
value: 2,
left: new NullHandlingTree(
value: 3,
right: new NullHandlingTree(value: 4),
left: new NullHandlingTree(value: 5)
)
)
println root.size()
println root.sum()
println root.product()
If we introduce the null object pattern (here by defining the NullTree
class), we can now simplify the logic in the size()
, sum()
and`product()` methods. These methods now much more clearly represent the logic for the normal (and now universal) case. Each of the methods within NullTree
returns a value which represents doing nothing.
class Tree {
def left = new NullTree(), right = new NullTree(), value
def size() {
1 + left.size() + right.size()
}
def sum() {
value + left.sum() + right.sum()
}
def product() {
value * left.product() * right.product()
}
}
class NullTree {
def size() { 0 }
def sum() { 0 }
def product() { 1 }
}
def root = new Tree(
value: 2,
left: new Tree(
value: 3,
right: new Tree(value: 4),
left: new Tree(value: 5)
)
)
println root.size()
println root.sum()
println root.product()
The result of running either of these examples is:
4 14 120
Note: a slight variation with the null object pattern is to combine it with the singleton pattern. So, we wouldn’t write new NullTree() wherever we needed a null object as shown above. Instead we would have a single null object instance which we would place within our data structures as needed.
1.14. Observer Pattern
The Observer Pattern allows one or more observers to be notified about changes or events from a subject object.
1.14.1. Example
Here is a typical implementation of the classic pattern:
interface Observer {
void update(message)
}
class Subject {
private List observers = []
void register(observer) {
observers << observer
}
void unregister(observer) {
observers -= observer
}
void notifyAll(message) {
observers.each{ it.update(message) }
}
}
class ConcreteObserver1 implements Observer {
def messages = []
void update(message) {
messages << message
}
}
class ConcreteObserver2 implements Observer {
def messages = []
void update(message) {
messages << message.toUpperCase()
}
}
def o1a = new ConcreteObserver1()
def o1b = new ConcreteObserver1()
def o2 = new ConcreteObserver2()
def observers = [o1a, o1b, o2]
new Subject().with {
register(o1a)
register(o2)
notifyAll('one')
}
new Subject().with {
register(o1b)
register(o2)
notifyAll('two')
}
def expected = [['one'], ['two'], ['ONE', 'TWO']]
assert observers*.messages == expected
Using Closures, we can avoid creating the concrete observer classes as shown below:
interface Observer {
void update(message)
}
class Subject {
private List observers = []
void register(Observer observer) {
observers << observer
}
void unregister(observer) {
observers -= observer
}
void notifyAll(message) {
observers.each{ it.update(message) }
}
}
def messages1a = [], messages1b = [], messages2 = []
def o2 = { messages2 << it.toUpperCase() }
new Subject().with {
register{ messages1a << it }
register(o2)
notifyAll('one')
}
new Subject().with {
register{ messages1b << it }
register(o2)
notifyAll('two')
}
def expected = [['one'], ['two'], ['ONE', 'TWO']]
assert [messages1a, messages1b, messages2] == expected
As a variation for Groovy 3+, let’s consider dropping the Observer
interface and using lambdas as shown below:
import java.util.function.Consumer
class Subject {
private List<Consumer> observers = []
void register(Consumer observer) {
observers << observer
}
void unregister(observer) {
observers -= observer
}
void notifyAll(message) {
observers.each{ it.accept(message) }
}
}
def messages1a = [], messages1b = [], messages2 = []
def o2 = { messages2 << it.toUpperCase() }
new Subject().with {
register(s -> messages1a << s)
register(s -> messages2 << s.toUpperCase())
notifyAll('one')
}
new Subject().with {
register(s -> messages1b << s)
register(s -> messages2 << s.toUpperCase())
notifyAll('two')
}
def expected = [['one'], ['two'], ['ONE', 'TWO']]
assert [messages1a, messages1b, messages2] == expected
We are now calling the accept
method from Consumer
rather
than the update
method from Observer
.
1.14.2. @Bindable and @Vetoable
The JDK has some built-in classes which follow the observer pattern.
The java.util.Observer
and java.util.Observable
classes are deprecated from JDK 9 due to various limitations.
Instead, you are recommended to use various more powerful classes in the java.beans
package such as java.beans.PropertyChangeListener
.
Luckily, Groovy has some built-in transforms (groovy.beans.Bindable and groovy.beans.Vetoable)
which support for some key classes from that package.
import groovy.beans.*
import java.beans.*
class PersonBean {
@Bindable String first
@Bindable String last
@Vetoable Integer age
}
def messages = [:].withDefault{[]}
new PersonBean().with {
addPropertyChangeListener{ PropertyChangeEvent ev ->
messages[ev.propertyName] << "prop: $ev.newValue"
}
addVetoableChangeListener{ PropertyChangeEvent ev ->
def name = ev.propertyName
if (name == 'age' && ev.newValue > 40)
throw new PropertyVetoException()
messages[name] << "veto: $ev.newValue"
}
first = 'John'
age = 35
last = 'Smith'
first = 'Jane'
age = 42
}
def expected = [
first:['prop: John', 'prop: Jane'],
age:['veto: 35'],
last:['prop: Smith']
]
assert messages == expected
Here, methods like addPropertyChangeListener
perform the same role as registerObserver
in previous examples.
There is a firePropertyChange
method corresponding to notifyAll
/notifyObservers
in previous examples but Groovy adds that
automatically here, so it isn’t visible in the source code. There is also a propertyChange
method that corresponds
to the update
method in previous examples, though again, that isn’t visible here.
1.15. Pimp my Library Pattern
The Pimp my Library Pattern suggests an approach for extending a library that nearly does everything that you need but just needs a little more. It assumes that you do not have source code for the library of interest.
1.15.1. Example
Suppose we want to make use of the built-in Integer facilities in Groovy (which build upon the features already in Java). Those libraries have nearly all of the features we want but not quite everything. We may not have all of the source code to the Groovy and Java libraries so we can’t just change the library. Instead we augment the library. Groovy has a number of ways to do this. One way is to use a Category.
First, we’ll define a suitable category.
class EnhancedInteger {
static boolean greaterThanAll(Integer self, Object[] others) {
greaterThanAll(self, others)
}
static boolean greaterThanAll(Integer self, others) {
others.every { self > it }
}
}
We have added two methods which augment the Integer methods by providing the greaterThanAll
method. Categories follow conventions where they are defined as static methods with a special first parameter representing the class we wish to extend. The greaterThanAll(Integer self, others) static method becomes the greaterThanAll(other)
instance method.
We defined two versions of greaterThanAll
. One which works for collections, ranges etc. The other which works with a variable number of Integer
arguments.
Here is how you would use the category.
use(EnhancedInteger) {
assert 4.greaterThanAll(1, 2, 3)
assert !5.greaterThanAll(2, 4, 6)
assert 5.greaterThanAll(-4..4)
assert 5.greaterThanAll([])
assert !5.greaterThanAll([4, 5])
}
As you can see, using this technique you can effectively enrich an original class without having access to its source code. Moreover, you can apply different enrichments in different parts of the system as well as work with un-enriched objects if we need to.
1.16. Proxy Pattern
The Proxy Pattern allows one object to act as a pretend replacement for some other object. In general, whoever is using the proxy, doesn’t realise that they are not using the real thing. The pattern is useful when the real object is hard to create or use: it may exist over a network connection, or be a large object in memory, or be a file, database or some other resource that is expensive or impossible to duplicate.
1.16.1. Example
One common use of the proxy pattern is when talking to remote objects in a different JVM. Here is the client code for creating a proxy that talks via sockets to a server object as well as an example usage:
class AccumulatorProxy {
def accumulate(args) {
def result
def s = new Socket("localhost", 54321)
s.withObjectStreams { ois, oos ->
oos << args
result = ois.readObject()
}
s.close()
return result
}
}
println new AccumulatorProxy().accumulate([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
// => 55
Here is what your server code might look like (start this first):
class Accumulator {
def accumulate(args) {
args.inject(0) { total, arg -> total += arg }
}
}
def port = 54321
def accumulator = new Accumulator()
def server = new ServerSocket(port)
println "Starting server on port $port"
while(true) {
server.accept() { socket ->
socket.withObjectStreams { ois, oos ->
def args = ois.readObject()
oos << accumulator.accumulate(args)
}
}
}
1.17. Singleton Pattern
The Singleton Pattern is used to make sure only one object of a particular class is ever created. This can be useful when exactly one object is needed to coordinate actions across a system; perhaps for efficiency where creating lots of identical objects would be wasteful, perhaps because a particular algorithm needing a single point of control is required or perhaps when an object is used to interact with a non-shareable resource.
Weaknesses of the Singleton pattern include:
-
It can reduce reuse. For instance, there are issues if you want to use inheritance with Singletons. If
SingletonB
extendsSingletonA
, should there be exactly (at most) one instance of each or should the creation of an object from one of the classes prohibit creation from the other. Also, if you decide both classes can have an instance, how do you override thegetInstance()
method which is static? -
It is also hard to test singletons in general because of the static methods but Groovy can support that if required.
1.17.1. Example: The Classic Java Singleton
Suppose we wish to create a class for collecting votes. Because getting the right number of votes may be very important, we decide to use the singleton pattern. There will only ever be one VoteCollector
object, so it makes it easier for us to reason about that objects creation and use.
class VoteCollector {
def votes = 0
private static final INSTANCE = new VoteCollector()
static getInstance() { return INSTANCE }
private VoteCollector() { }
def display() { println "Collector:${hashCode()}, Votes:$votes" }
}
Some points of interest in this code:
-
it has a private constructor, so no
VoteCollector
objects can be created in our system (except for theINSTANCE
we create) -
the
INSTANCE
is also private, so it can’t be changed once set -
we haven’t made the updating of votes thread-safe at this point (it doesn’t add to this example)
-
the vote collector instance is not lazily created (if we never reference the class, the instance won’t be created; however, as soon as we reference the class, the instance will be created even if not needed initially)
We can use this singleton class in some script code as follows:
def collector = VoteCollector.instance
collector.display()
collector.votes++
collector = null
Thread.start{
def collector2 = VoteCollector.instance
collector2.display()
collector2.votes++
collector2 = null
}.join()
def collector3 = VoteCollector.instance
collector3.display()
Here we used the instance 3 times. The second usage was even in a different thread (but don’t try this in a scenario with a new class loader).
Running this script yields (your hashcode value will vary):
Collector:15959960, Votes:0 Collector:15959960, Votes:1 Collector:15959960, Votes:2
Variations to this pattern:
-
To support lazy-loading and multi-threading, we could just use the
synchronized
keyword with thegetInstance()
method. This has a performance hit but will work. -
We can consider variations involving double-checked locking and the
volatile
keyword, but see the limitations of this approach here.
1.17.2. Example: Singleton via MetaProgramming
Groovy’s meta-programming capabilities allow concepts like the singleton pattern to be enacted in a far more fundamental way. This example illustrates a simple way to use Groovy’s meta-programming capabilities to achieve the singleton pattern but not necessarily the most efficient way.
Suppose we want to keep track of the total number of calculations that a calculator performs. One way to do that is to use a singleton for the calculator class and keep a variable in the class with the count.
First we define some base classes. A Calculator
class which performs calculations and records how many such calculations it performs and a Client
class which acts as a facade to the calculator.
class Calculator {
private total = 0
def add(a, b) { total++; a + b }
def getTotalCalculations() { 'Total Calculations: ' + total }
String toString() { 'Calc: ' + hashCode() }
}
class Client {
def calc = new Calculator()
def executeCalc(a, b) { calc.add(a, b) }
String toString() { 'Client: ' + hashCode() }
}
Now we can define and register a MetaClass which intercepts all attempts to create a Calculator
object and always provides a pre-created instance instead. We also register this MetaClass with the Groovy system:
class CalculatorMetaClass extends MetaClassImpl {
private static final INSTANCE = new Calculator()
CalculatorMetaClass() { super(Calculator) }
def invokeConstructor(Object[] arguments) { return INSTANCE }
}
def registry = GroovySystem.metaClassRegistry
registry.setMetaClass(Calculator, new CalculatorMetaClass())
Now we use instances of our Client
class from within a script. The client class will attempt to create new instances of the calculator but will always get the singleton.
def client = new Client()
assert 3 == client.executeCalc(1, 2)
println "$client, $client.calc, $client.calc.totalCalculations"
client = new Client()
assert 4 == client.executeCalc(2, 2)
println "$client, $client.calc, $client.calc.totalCalculations"
Here is the result of running this script (your hashcode values may vary):
Client: 7306473, Calc: 24230857, Total Calculations: 1 Client: 31436753, Calc: 24230857, Total Calculations: 2
1.17.3. Guice Example
We can also implement the Singleton Pattern using Guice.
Consider the Calculator example again.
Guice is a Java-oriented framework that supports Interface-Oriented design. Hence we create a Calculator
interface first. We can then create our CalculatorImpl
implementation and a Client
object which our script will interact with. The Client
class isn’t strictly needed for this example but allows us to show that non-singleton instances are the default. Here is the code:
@Grapes([@Grab('aopalliance:aopalliance:1.0'), @Grab('com.google.code.guice:guice:1.0')])
import com.google.inject.*
interface Calculator {
def add(a, b)
}
class CalculatorImpl implements Calculator {
private total = 0
def add(a, b) { total++; a + b }
def getTotalCalculations() { 'Total Calculations: ' + total }
String toString() { 'Calc: ' + hashCode() }
}
class Client {
@Inject Calculator calc
def executeCalc(a, b) { calc.add(a, b) }
String toString() { 'Client: ' + hashCode() }
}
def injector = Guice.createInjector (
[configure: { binding ->
binding.bind(Calculator)
.to(CalculatorImpl)
.asEagerSingleton() } ] as Module
)
def client = injector.getInstance(Client)
assert 3 == client.executeCalc(1, 2)
println "$client, $client.calc, $client.calc.totalCalculations"
client = injector.getInstance(Client)
assert 4 == client.executeCalc(2, 2)
println "$client, $client.calc, $client.calc.totalCalculations"
Note the @Inject
annotation in the Client
class. We can always tell right in the source code which fields will be injected.
In this example we chose to use an explicit binding. All of our dependencies (ok, only one in this example at the moment) are configured in the binding. The Guide injector knows about the binding and injects the dependencies as required when we create objects. For the singleton pattern to hold, you must always use Guice to create your instances. Nothing shown so far would stop you creating another instance of the calculator manually using new CalculatorImpl() which would of course violate the desired singleton behaviour.
In other scenarios (though probably not in large systems), we could choose to express dependencies using annotations, such as the following example shows:
@Grapes([@Grab('aopalliance:aopalliance:1.0'), @Grab('com.google.code.guice:guice:1.0')])
import com.google.inject.*
@ImplementedBy(CalculatorImpl)
interface Calculator {
// as before ...
}
@Singleton
class CalculatorImpl implements Calculator {
// as before ...
}
class Client {
// as before ...
}
def injector = Guice.createInjector()
// ...
Note the @Singleton
annotation on the CalculatorImpl
class and the @ImplementedBy
annotation in the Calculator
interface.
When run, the above example (using either approach) yields (your hashcode values will vary):
Client: 8897128, Calc: 17431955, Total Calculations: 1 Client: 21145613, Calc: 17431955, Total Calculations: 2
You can see that we obtained a new client object whenever we asked for an instance but it was injected with the same calculator object.
1.17.4. Spring Example
We can do the Calculator example again using Spring as follows:
@Grapes([@Grab('org.springframework:spring-core:5.2.8.RELEASE'), @Grab('org.springframework:spring-beans:5.2.8.RELEASE')])
import org.springframework.beans.factory.support.*
interface Calculator {
def add(a, b)
}
class CalculatorImpl implements Calculator {
private total = 0
def add(a, b) { total++; a + b }
def getTotalCalculations() { 'Total Calculations: ' + total }
String toString() { 'Calc: ' + hashCode() }
}
class Client {
Client(Calculator calc) { this.calc = calc }
def calc
def executeCalc(a, b) { calc.add(a, b) }
String toString() { 'Client: ' + hashCode() }
}
// Here we 'wire' up our dependencies through the API. Alternatively,
// we could use XML-based configuration or the Grails Bean Builder DSL.
def factory = new DefaultListableBeanFactory()
factory.registerBeanDefinition('calc', new RootBeanDefinition(CalculatorImpl))
def beanDef = new RootBeanDefinition(Client, false)
beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_AUTODETECT)
factory.registerBeanDefinition('client', beanDef)
def client = factory.getBean('client')
assert 3 == client.executeCalc(1, 2)
println "$client, $client.calc, $client.calc.totalCalculations"
client = factory.getBean('client')
assert 4 == client.executeCalc(2, 2)
println "$client, $client.calc, $client.calc.totalCalculations"
And here is the result (your hashcode values will vary):
Client: 29418586, Calc: 10580099, Total Calculations: 1 Client: 14800362, Calc: 10580099, Total Calculations: 2
1.18. State Pattern
The State Pattern provides a structured approach to partitioning the behaviour within complex systems. The overall behaviour of a system is partitioned into well-defined states. Typically, each state is implemented by a class. The overall system behaviour can be determined firstly by knowing the current state of the system; secondly, by understanding the behaviour possible while in that state (as embodied in the methods of the class corresponding to that state).
1.18.1. Example
Here is an example:
class Client {
def context = new Context()
def connect() {
context.state.connect()
}
def disconnect() {
context.state.disconnect()
}
def send_message(message) {
context.state.send_message(message)
}
def receive_message() {
context.state.receive_message()
}
}
class Context {
def state = new Offline(this)
}
class ClientState {
def context
ClientState(context) {
this.context = context
inform()
}
}
class Offline extends ClientState {
Offline(context) {
super(context)
}
def inform() {
println "offline"
}
def connect() {
context.state = new Online(context)
}
def disconnect() {
println "error: not connected"
}
def send_message(message) {
println "error: not connected"
}
def receive_message() {
println "error: not connected"
}
}
class Online extends ClientState {
Online(context) {
super(context)
}
def inform() {
println "connected"
}
def connect() {
println "error: already connected"
}
def disconnect() {
context.state = new Offline(context)
}
def send_message(message) {
println "\"$message\" sent"
}
def receive_message() {
println "message received"
}
}
client = new Client()
client.send_message("Hello")
client.connect()
client.send_message("Hello")
client.connect()
client.receive_message()
client.disconnect()
Here is the output:
offline error: not connected connected "Hello" sent error: already connected message received offline
One of the great things about a dynamic language like Groovy though is that we can take this example and express it in many different ways depending on our particular needs. Some potential variations for this example are shown below.
1.18.2. Variation 1: Leveraging Interface-Oriented Design
One approach we could take is to leverage Interface-Oriented Design. To do this, we could introduce the following interface:
interface State {
def connect()
def disconnect()
def send_message(message)
def receive_message()
}
Then our Client
, Online
and 'Offline` classes could be modified to implement that interface, e.g.:
class Client implements State {
// ... as before ...
}
class Online implements State {
// ... as before ...
}
class Offline implements State {
// ... as before ...
}
You might ask: Haven’t we just introduced additional boilerplate code? Can’t we rely on duck-typing for this? The answer is 'yes' and 'no'. We can get away with duck-typing but one of the key intentions of the state pattern is to partition complexity. If we know that the client class and each state class all satisfy one interface, then we have placed some key boundaries around the complexity. We can look at any state class in isolation and know the bounds of behaviour possible for that state.
We don’t have to use interfaces for this, but it helps express the intent of this particular style of partitioning and it helps reduce the size of our unit tests (we would have to have additional tests in place to express this intent in languages which have less support for interface-oriented design).
1.18.3. Variation 2: Extract State Pattern Logic
Alternatively, or in combination with other variations, we might decide to extract some of our State Pattern logic into helper classes. For example, we could define the following classes in a state pattern package/jar/script:
abstract class InstanceProvider {
static def registry = GroovySystem.metaClassRegistry
static def create(objectClass, param) {
registry.getMetaClass(objectClass).invokeConstructor([param] as Object[])
}
}
abstract class Context {
private context
protected setContext(context) {
this.context = context
}
def invokeMethod(String name, Object arg) {
context.invokeMethod(name, arg)
}
def startFrom(initialState) {
setContext(InstanceProvider.create(initialState, this))
}
}
abstract class State {
private client
State(client) { this.client = client }
def transitionTo(nextState) {
client.setContext(InstanceProvider.create(nextState, client))
}
}
This is all quite generic and can be used wherever we want to introduce the state pattern. Here is what our code would look like now:
class Client extends Context {
Client() {
startFrom(Offline)
}
}
class Offline extends State {
Offline(client) {
super(client)
println "offline"
}
def connect() {
transitionTo(Online)
}
def disconnect() {
println "error: not connected"
}
def send_message(message) {
println "error: not connected"
}
def receive_message() {
println "error: not connected"
}
}
class Online extends State {
Online(client) {
super(client)
println "connected"
}
def connect() {
println "error: already connected"
}
def disconnect() {
transitionTo(Offline)
}
def send_message(message) {
println "\"$message\" sent"
}
def receive_message() {
println "message received"
}
}
client = new Client()
client.send_message("Hello")
client.connect()
client.send_message("Hello")
client.connect()
client.receive_message()
client.disconnect()
You can see here the startFrom
and transitionTo
methods begin to give our example code a DSL feel.
1.18.4. Variation 3: Bring on the DSL
Alternatively, or in combination with other variations, we might decide to fully embrace a Domain Specific Language (DSL) approach to this example.
We can define the following generic helper functions (first discussed here):
class Grammar {
def fsm
def event
def fromState
def toState
Grammar(a_fsm) {
fsm = a_fsm
}
def on(a_event) {
event = a_event
this
}
def on(a_event, a_transitioner) {
on(a_event)
a_transitioner.delegate = this
a_transitioner.call()
this
}
def from(a_fromState) {
fromState = a_fromState
this
}
def to(a_toState) {
assert a_toState, "Invalid toState: $a_toState"
toState = a_toState
fsm.registerTransition(this)
this
}
def isValid() {
event && fromState && toState
}
public String toString() {
"$event: $fromState=>$toState"
}
}
class FiniteStateMachine {
def transitions = [:]
def initialState
def currentState
FiniteStateMachine(a_initialState) {
assert a_initialState, "You need to provide an initial state"
initialState = a_initialState
currentState = a_initialState
}
def record() {
Grammar.newInstance(this)
}
def reset() {
currentState = initialState
}
def isState(a_state) {
currentState == a_state
}
def registerTransition(a_grammar) {
assert a_grammar.isValid(), "Invalid transition ($a_grammar)"
def transition
def event = a_grammar.event
def fromState = a_grammar.fromState
def toState = a_grammar.toState
if (!transitions[event]) {
transitions[event] = [:]
}
transition = transitions[event]
assert !transition[fromState], "Duplicate fromState $fromState for transition $a_grammar"
transition[fromState] = toState
}
def fire(a_event) {
assert currentState, "Invalid current state '$currentState': passed into constructor"
assert transitions.containsKey(a_event), "Invalid event '$a_event', should be one of ${transitions.keySet()}"
def transition = transitions[a_event]
def nextState = transition[currentState]
assert nextState, "There is no transition from '$currentState' to any other state"
currentState = nextState
currentState
}
}
Now we can define and test our state machine like this:
class StatePatternDslTest extends GroovyTestCase {
private fsm
protected void setUp() {
fsm = FiniteStateMachine.newInstance('offline')
def recorder = fsm.record()
recorder.on('connect').from('offline').to('online')
recorder.on('disconnect').from('online').to('offline')
recorder.on('send_message').from('online').to('online')
recorder.on('receive_message').from('online').to('online')
}
void testInitialState() {
assert fsm.isState('offline')
}
void testOfflineState() {
shouldFail{
fsm.fire('send_message')
}
shouldFail{
fsm.fire('receive_message')
}
shouldFail{
fsm.fire('disconnect')
}
assert 'online' == fsm.fire('connect')
}
void testOnlineState() {
fsm.fire('connect')
fsm.fire('send_message')
fsm.fire('receive_message')
shouldFail{
fsm.fire('connect')
}
assert 'offline' == fsm.fire('disconnect')
}
}
This example isn’t an exact equivalent of the others. It doesn’t use predefined Online
and Offline
classes.
Instead, it defines the entire state machine on the fly as needed.
See the previous reference for more elaborate examples of this style.
See also: Model-based testing using ModelJUnit
1.19. Strategy Pattern
The Strategy Pattern allows you to abstract away particular algorithms from their usage. This allows you to easily swap the algorithm being used without having to change the calling code. The general form of the pattern is:
In Groovy, because of its ability to treat code as a first class object using anonymous methods (which we loosely call Closures), the need for the strategy pattern is greatly reduced. You can simply place algorithms inside Closures.
1.19.1. Example using traditional class hierarchy
First let’s look at the traditional way of encapsulating the Strategy Pattern.
interface Calc {
def execute(n, m)
}
class CalcByMult implements Calc {
def execute(n, m) { n * m }
}
class CalcByManyAdds implements Calc {
def execute(n, m) {
def result = 0
n.times{
result += m
}
result
}
}
def sampleData = [
[3, 4, 12],
[5, -5, -25]
]
Calc[] multiplicationStrategies = [
new CalcByMult(),
new CalcByManyAdds()
]
sampleData.each{ data ->
multiplicationStrategies.each { calc ->
assert data[2] == calc.execute(data[0], data[1])
}
}
Here we have defined an interface Calc
which our concrete strategy classes will implement (we could also have used an abstract class).
We then defined two algorithms for doing simple multiplication: CalcByMult
the normal way, and CalcByManyAdds using only addition (don’t try this one using negative numbers - yes we could fix this but it would just make the example longer).
We then use normal polymorphism to invoke the algorithms.
1.19.2. Example using closures
Here is the Groovier way to achieve the same thing using Closures:
def multiplicationStrategies = [
{ n, m -> n * m },
{ n, m -> def result = 0; n.times{ result += m }; result }
]
def sampleData = [
[3, 4, 12],
[5, -5, -25]
]
sampleData.each{ data ->
multiplicationStrategies.each { calc ->
assert data[2] == calc(data[0], data[1])
}
}
1.19.3. Example using lambdas
For Groovy 3+, we can leverage lambda syntax:
interface Calc {
def execute(n, m)
}
List<Calc> multiplicationStrategies = [
(n, m) -> n * m,
(n, m) -> { def result = 0; n.times{ result += m }; result }
]
def sampleData = [
[3, 4, 12],
[5, -5, -25]
]
sampleData.each{ data ->
multiplicationStrategies.each { calc ->
assert data[2] == calc(data[0], data[1])
}
}
Or we can use the built-in JDK BiFunction
class:
import java.util.function.BiFunction
List<BiFunction<Integer, Integer, Integer>> multiplicationStrategies = [
(n, m) -> n * m,
(n, m) -> { def result = 0; n.times{ result += m }; result }
]
def sampleData = [
[3, 4, 12],
[5, -5, -25]
]
sampleData.each{ data ->
multiplicationStrategies.each { calc ->
assert data[2] == calc(data[0], data[1])
}
}
1.20. Template Method Pattern
The Template Method Pattern abstracts away the details of several algorithms. The generic part of an algorithm is contained within a base class. Particular implementation details are captured within child classes. The generic pattern of classes involved looks like this:
1.20.1. Example with traditional classes
In this example, the base Accumulator
class captures the essence of the accumulation algorithm.
The child classes Sum
and Product
provide particular customised ways to use the generic accumulation algorithm.
abstract class Accumulator {
protected initial
abstract doAccumulate(total, v)
def accumulate(values) {
def total = initial
values.each { v -> total = doAccumulate(total, v) }
total
}
}
class Sum extends Accumulator {
def Sum() { initial = 0 }
def doAccumulate(total, v) { total + v }
}
class Product extends Accumulator {
def Product() { initial = 1 }
def doAccumulate(total, v) { total * v }
}
assert 10 == new Sum().accumulate([1,2,3,4])
assert 24 == new Product().accumulate([1,2,3,4])
1.20.2. Example with simplifying strategies
In this particular case, you could use Groovy’s inject method to achieve a similar result using Closures:
Closure addAll = { total, item -> total += item }
def accumulated = [1, 2, 3, 4].inject(0, addAll)
assert accumulated == 10
Thanks to duck-typing, this would also work with other objects which support an add (plus()
in Groovy) method, e.g.:
accumulated = [ "1", "2", "3", "4" ].inject("", addAll)
assert accumulated == "1234"
We could also do the multiplication case as follows (re-writing as a one-liner):
assert 24 == [1, 2, 3, 4].inject(1) { total, item -> total *= item }
Using closures this way looks like the Strategy Pattern, but if we realise
that Groovy’s inject
method is the generic part of the algorithm for our template method,
then the Closures become the customised parts of the template method pattern.
For Groovy 3+, we can use lambda syntax as an alternative to the closure syntax:
assert 10 == [1, 2, 3, 4].stream().reduce(0, (l, r) -> l + r)
assert 24 == [1, 2, 3, 4].stream().reduce(1, (l, r) -> l * r)
assert '1234' == ['1', '2', '3', '4'].stream().reduce('', (l, r) -> l + r)
Here the stream api’s reduce
method is the generic part of the algorithm for our template method,
and the lambdas are the customised parts of the template method pattern.
1.21. Visitor Pattern
The Visitor Pattern is one of those well-known but not often used patterns. Perhaps this is because it seems a little complex at first. But once you become familiar with it, it becomes a powerful way to evolve your code and as we’ll see, Groovy provides ways to reduce some to the complexity, so there is no reason not to consider using this pattern.
The goal of the pattern is to separate an algorithm from an object structure. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures.
1.21.1. Simple Example
This example considers how to calculate the bounds of shapes (or collections of shapes). Our first attempt uses the traditional visitor pattern. We will see a more Groovy way to do this shortly.
abstract class Shape { }
@ToString(includeNames=true)
class Rectangle extends Shape {
def x, y, w, h
Rectangle(x, y, w, h) {
this.x = x; this.y = y; this.w = w; this.h = h
}
def union(rect) {
if (!rect) return this
def minx = [rect.x, x].min()
def maxx = [rect.x + rect.w, x + w].max()
def miny = [rect.y, y].min()
def maxy = [rect.y + rect.h, y + h].max()
new Rectangle(minx, miny, maxx - minx, maxy - miny)
}
def accept(visitor) {
visitor.visit_rectangle(this)
}
}
class Line extends Shape {
def x1, y1, x2, y2
Line(x1, y1, x2, y2) {
this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2
}
def accept(visitor){
visitor.visit_line(this)
}
}
class Group extends Shape {
def shapes = []
def add(shape) { shapes += shape }
def remove(shape) { shapes -= shape }
def accept(visitor) {
visitor.visit_group(this)
}
}
class BoundingRectangleVisitor {
def bounds
def visit_rectangle(rectangle) {
if (bounds)
bounds = bounds.union(rectangle)
else
bounds = rectangle
}
def visit_line(line) {
def line_bounds = new Rectangle([line.x1, line.x2].min(),
[line.y1, line.y2].min(),
line.x2 - line.y1,
line.x2 - line.y2)
if (bounds)
bounds = bounds.union(line_bounds)
else
bounds = line_bounds
}
def visit_group(group) {
group.shapes.each { shape -> shape.accept(this) }
}
}
def group = new Group()
group.add(new Rectangle(100, 40, 10, 5))
group.add(new Rectangle(100, 70, 10, 5))
group.add(new Line(90, 30, 60, 5))
def visitor = new BoundingRectangleVisitor()
group.accept(visitor)
bounding_box = visitor.bounds
assert bounding_box.toString() == 'Rectangle(x:60, y:5, w:50, h:70)'
That took quite a bit of code, but the idea now is that we could add further algorithms just by adding new visitors with our shape classes remaining unchanged, e.g. we could add a total area visitor or a collision detection visitor.
We can improve the clarity of our code (and shrink it to about half the size) by making use of Groovy Closures as follows:
abstract class Shape {
def accept(Closure yield) { yield(this) }
}
@ToString(includeNames=true)
class Rectangle extends Shape {
def x, y, w, h
def bounds() { this }
def union(rect) {
if (!rect) return this
def minx = [ rect.x, x ].min()
def maxx = [ rect.x + rect.w, x + w ].max()
def miny = [ rect.y, y ].min()
def maxy = [ rect.y + rect.h, y + h ].max()
new Rectangle(x:minx, y:miny, w:maxx - minx, h:maxy - miny)
}
}
class Line extends Shape {
def x1, y1, x2, y2
def bounds() {
new Rectangle(x:[x1, x2].min(), y:[y1, y2].min(),
w:(x2 - x1).abs(), h:(y2 - y1).abs())
}
}
class Group {
def shapes = []
def leftShift(shape) { shapes += shape }
def accept(Closure yield) { shapes.each{it.accept(yield)} }
}
def group = new Group()
group << new Rectangle(x:100, y:40, w:10, h:5)
group << new Rectangle(x:100, y:70, w:10, h:5)
group << new Line(x1:90, y1:30, x2:60, y2:5)
def bounds
group.accept{ bounds = it.bounds().union(bounds) }
assert bounds.toString() == 'Rectangle(x:60, y:5, w:50, h:70)'
Or, using lambdas as follows:
abstract class Shape {
def accept(Function<Shape, Shape> yield) { yield.apply(this) }
}
@ToString(includeNames=true)
class Rectangle extends Shape {
/* ... same as with Closures ... */
}
class Line extends Shape {
/* ... same as with Closures ... */
}
class Group {
def shapes = []
def leftShift(shape) { shapes += shape }
def accept(Function<Shape, Shape> yield) {
shapes.stream().forEach(s -> s.accept(yield))
}
}
def group = new Group()
group << new Rectangle(x:100, y:40, w:10, h:5)
group << new Rectangle(x:100, y:70, w:10, h:5)
group << new Line(x1:90, y1:30, x2:60, y2:5)
def bounds
group.accept(s -> { bounds = s.bounds().union(bounds) })
assert bounds.toString() == 'Rectangle(x:60, y:5, w:50, h:70)'
1.21.2. Advanced Example
Let’s consider another example to illustrate some more points about this pattern.
interface Visitor {
void visit(NodeType1 n1)
void visit(NodeType2 n2)
}
interface Visitable {
void accept(Visitor visitor)
}
class NodeType1 implements Visitable {
Visitable[] children = new Visitable[0]
void accept(Visitor visitor) {
visitor.visit(this)
for(int i = 0; i < children.length; ++i) {
children[i].accept(visitor)
}
}
}
class NodeType2 implements Visitable {
Visitable[] children = new Visitable[0]
void accept(Visitor visitor) {
visitor.visit(this)
for(int i = 0; i < children.length; ++i) {
children[i].accept(visitor)
}
}
}
class NodeType1Counter implements Visitor {
int count = 0
void visit(NodeType1 n1) {
count++
}
void visit(NodeType2 n2){}
}
If we now use NodeType1Counter
on a tree like this:
NodeType1 root = new NodeType1()
root.children = new Visitable[]{new NodeType1(), new NodeType2()}
def counter = new NodeType1Counter()
root.accept(counter)
assert counter.count == 2
Then we have one NodeType1
object as root and one of the children is also a NodeType1
instance.
The other child is a NodeType2
instance.
That means using NodeType1Counter
here should count 2 NodeType1
objects as the last statement verifies.
When to use this
This example illustrates some of the advantages of the visitor pattern.
For example, while our visitor has state (the count of NodeType1
objects), the tree of objects itself is not changed.
Similarly, if we wanted to have a visitor counting all node types,
or one that counts how many different types are used, or one that gathers information
using methods special to the node types, again, the visitor alone is all that would need to be written.
What happens if we add a new type?
In this case we might have a fair bit of work to do. We probably have to change the Visitor
interface to accept the new type,
and change potentially most existing visitors based on that interface change,
and we have to write the new type itself.
A better approach is to write a default implementation of the visitor which all concrete visitors will extend.
We’ll see this approach in use shortly.
What if we want to have different iteration patterns?
Then you have a problem. Since the node describes how to iterate, you have no influence and stop iteration at a point or change the order. So maybe we should change this a little to this:
interface Visitor {
void visit(NodeType1 n1)
void visit(NodeType2 n2)
}
class DefaultVisitor implements Visitor{
void visit(NodeType1 n1) {
for(int i = 0; i < n1.children.length; ++i) {
n1.children[i].accept(this)
}
}
void visit(NodeType2 n2) {
for(int i = 0; i < n2.children.length; ++i) {
n2.children[i].accept(this)
}
}
}
interface Visitable {
void accept(Visitor visitor)
}
class NodeType1 implements Visitable {
Visitable[] children = new Visitable[0]
void accept(Visitor visitor) {
visitor.visit(this)
}
}
class NodeType2 implements Visitable {
Visitable[] children = new Visitable[0];
void accept(Visitor visitor) {
visitor.visit(this)
}
}
class NodeType1Counter extends DefaultVisitor {
int count = 0
void visit(NodeType1 n1) {
count++
super.visit(n1)
}
}
Some small changes but with big effect.
The visitor is now recursive and tells me how to iterate.
The implementation in the Nodes is minimized to visitor.visit(this)
, DefaultVisitor
is now able to catch the new types, we can stop iteration by not delegating to super.
Of course the big disadvantage now is that it is no longer iterative, but you can’t get all the benefits.
Make it Groovy
The question now is how to make that a bit more Groovy.
Didn’t you find this visitor.visit(this)
strange? Why is it there?
The answer is to simulate double dispatch.
In Java, the compile time type is used, so for visitor.visit(children[i])
the compiler won’t be
able to find the correct method, because Visitor
does not contain a method visit(Visitable)
.
And even if it would, we would like to visit the more special methods with NodeType1
or NodeType2
.
Now Groovy is not using the static type, Groovy uses the runtime type.
This means we can use visitor.visit(children[i])
without any problem.
Since we minimized the accept method to just do the double dispatch part and
since the runtime type system of Groovy will already cover that, do we need the accept method?
Not really, but we can do even more.
We had the disadvantage of not knowing how to handle unknown tree elements.
We had to extend the interface Visitor
for that, resulting in changes to DefaultVisitor
and
then we have the task to provide a useful default like iterating the node or not doing anything at all.
Now with Groovy we can catch that case by adding a visit(Visitable)
method that does nothing.
That would be the same in Java btw.
But don’t let us stop here. Do we need the Visitor
interface?
If we don’t have the accept method, then we don’t need the Visitor
interface at all.
So the new code would be:
class DefaultVisitor {
void visit(NodeType1 n1) {
n1.children.each { visit(it) }
}
void visit(NodeType2 n2) {
n2.children.each { visit(it) }
}
void visit(Visitable v) { }
}
interface Visitable { }
class NodeType1 implements Visitable {
Visitable[] children = []
}
class NodeType2 implements Visitable {
Visitable[] children = []
}
class NodeType1Counter extends DefaultVisitor {
int count = 0
void visit(NodeType1 n1) {
count++
super.visit(n1)
}
}
Looks like we saved a few lines of code here, but we made more.
The Visitable
nodes now do not refer to any Visitor
class or interface.
This is about the best level of separation you might expect here, but we can go further.
Let’s change the Visitable
interface a little and let it return the children we want to visit next.
This allows us a general iteration method.
class DefaultVisitor {
void visit(Visitable v) {
doIteration(v)
}
void doIteration(Visitable v) {
v.children.each {
visit(it)
}
}
}
interface Visitable {
Visitable[] getChildren()
}
class NodeType1 implements Visitable {
Visitable[] children = []
}
class NodeType2 implements Visitable {
Visitable[] children = []
}
class NodeType1Counter extends DefaultVisitor {
int count = 0
void visit(NodeType1 n1) {
count++
super.visit(n1)
}
}
DefaultVisitor
now looks a bit different.
It has a doIteration
method that will get the children it should iterate over and then call visit on each element.
Per default this will call visit(Visitable)
which then iterates over the children of this child.
Visitable
has also changed to ensure that any node will be able to return children (even if empty).
We didn’t have to change the NodeType1
and NodeType2
class, because the way the children field was
defined already made them a property, which means Groovy is so nice to generate a get method for us.
Now the really interesting part is NodeType1Counter
, it is interesting because we have not changed it.
super.visit(n1)
will now call visit(Visitable)
which will call doIteration
which will start the next level of iteration.
So no change. But visit(it)
will call visit(NodeType1)
if it is of type NodeType1
.
In fact, we don’t need the doIteration
method, we could do that in visit(Visitable)
too,
but this variant has some benefits. It allows us to write a new Visitor
that overwrites visit(Visitable
)
for error cases which of course means we must not do super.visit(n1)
but doIteration(n1)
.
Summary
In the end we got ~40% less code, a robust and stable architecture, and we completely removed the Visitor from the Visitable. To achieve the same in Java, you would probably need to resort to reflection.
The visitor pattern has sometimes been described as a poor fit for extreme programming techniques because you need to make changes to so many classes all the time. With our design, if we add new types we don’t need to change anything. So, the pattern is a good fit for agile approaches when using Groovy.
There are variants of the Visitor pattern, like the acyclic visitor pattern,
that try to solve the problem of adding new node types with special visitors.
The implementations of these visitors have their own code smells, like using casts, overuse of instanceof
, and other tricks.
What’s more the problems such approaches are trying to solve don’t occur within the Groovy version. We recommend avoiding that variant of this pattern.
Finally, in case it isn’t obvious, NodeType1Counter
could be implemented in Java as well.
Groovy will recognize the visit methods and call them as needed because DefaultVisitor
is still Groovy and does all the magic.
2. References
-
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 0-201-63361-2.
-
The canonical reference of design patterns.
-
-
Martin Fowler (1999). Refactoring: Improving the Design of Existing Code. Addison-Wesley. ISBN 0-201-48567-2.
-
Joshua Kerievsky (2004). Refactoring To Patterns. Addison-Wesley. ISBN 0-321-21335-1.
-
Eric Freeman, Elisabeth Freeman, Kathy Sierra, Bert Bates (2004). Head First Design Patterns. O’Reilly. ISBN 0-596-00712-4. *A great book to read, informative as well as amusing.
-
Dierk Koenig with Andrew Glover, Paul King, Guillaume Laforge and Jon Skeet (2007). Groovy in Action. Manning. ISBN 1-932394-84-2.
-
Discusses Visitor, Builder and other Patterns.
-
-
Brad Appleton (1999). Pizza Inversion - a Pattern for Efficient Resource Consumption.
-
One of the most frequently used patterns by many software engineers!
-
-
Design Patterns in Dynamic Languages by Neil Ford. Houston Java User’s Group. Examples in Groovy and Ruby. http://www.oracle.com/technetwork/server-storage/ts-4961-159222.pdf