blob: 9fdf7f3dcdf69486b279b9cea88e75c3f17e14c3 [file] [log] [blame]
//////////////////////////////////////////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
//////////////////////////////////////////
= Command Pattern
The https://en.wikipedia.org/wiki/Command_pattern[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:
[plantuml, CommandClasses, png]
....
!pragma layout smetana
skinparam nodesep 100
hide fields
interface Command {
+execute(String command)
}
object client
class Command1 implements Command {
+execute(String command)
}
class Receiver1 {
+action1(args1)
}
client ..r..> "command" Command
Command1 --r--> Receiver1
....
The sequence of interactions is as shown below for an arbitrary receiver:
[plantuml, CommandSequence, png]
....
!pragma layout smetana
client -> intermediary: command
intermediary -> receiverN: actionN
....
== 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:
[source,groovy]
----
include::../test/DesignPatternsTest.groovy[tags=command_traditional,indent=0]
----
Our client scripts sends `execute` commands to an intermediary and knows nothing
about any specific receivers, or any specific action method names and arguments.
== 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:
[source,groovy]
----
include::../test/DesignPatternsTest.groovy[tags=command_closures,indent=0]
----
<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:
[source,groovy]
----
include::../test/DesignPatternsTest.groovy[tags=command_lambda,indent=0]
----
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:
[source,groovy]
----
include::../test/DesignPatternsTest.groovy[tags=command_lambda_variant,indent=0]
----