README
The
REFCODES.ORG
codes represent a group of artifacts consolidating parts of my work in the past years. Several topics are covered which I consider useful for you, programmers, developers and software engineers.
What is this repository for?
Basically a Command
can be seen as a method
turned inside out: This artifact provides you means to implement your custom commands as of the command pattern.
“… (with) the command pattern … an object is used to represent and encapsulate all the information needed to call a method at a later time. This … includes the … method parameters …” (see Command pattern at Wikipedia)
How do I get set up?
To get up and running, include the following dependency (without the three dots “…”) in your pom.xml
:
1
2
3
4
5
6
7
8
9
<dependencies>
...
<dependency>
<artifactId>refcodes-command</artifactId>
<groupId>org.refcodes</groupId>
<version>3.3.9</version>
</dependency>
...
</dependencies>
The artifact is hosted directly at Maven Central. Jump straight to the source codes at Bitbucket. Read the artifact’s javadoc at javadoc.io.
How do I get started?
A Command
can be seen as a method and its context (variables) all transformed to an object (e.g a method turned inside out).
Given you define a Command
’s interface with an execute
and an undo
method and you strictly make use of commands: Then you easily can provide undo functionality by putting your executed Command
s onto a stack
…
A Command
respectively an Undoable
represents an (atomic) operation applied to a context
encapsulated in an object
(as of object oriented programming). Usually a Command
(Undoable
) also provides means to undo its operation applied before. The Command
(Undoable
) is created by a client (e.g. the business logic) and passed e.g. to a command-bus for execution or executed “manually” at required / desired time.
You find a command-bus implementation with the refcodes-jobbus
artifact (see type JobBus
).
Above see the execute
method as of a (generic) Command
defined by the refcodes-command
artifact. See below in case the Command
provides undo functionality (called Undoable
by the refcodes-command
artifact):
A client
is the business logic creating an Undoable
(job
) to be executed. The context
(e.g. provided by the client
to the Command
) can be a service
, a service-bus
(providing a handful of services), a component
or a plain POJO
(this depends on your requirements and your implementation).
Snippets of interest
Below find some code snippets which demonstrate the various aspects of using the refcodes-command
artifact (and , if applicable, its offsprings). See also the example source codes of this artifact for further information on the usage of this artifact.
An example
For the complete source codes of the below example see the tests in the
refcodes-command
artifact on bitbucket.
To show you some basic example code, given you have a list of integers and want to apply operations such as add
, subtract
or multiply
on that list. You can apply as many of those operations as you like on that list of integers. The add
operation would add a given value to each of the integer elements contained in that list, subtract
would subtract a given value from each of the integer elements contained in that list, and multiply
would multiply each integer element in that list with a given value:
As an example we are building a calculator applying its arithmetic operations on a list of values. A user of that “list” calculator is to be able to undo each operation applied on such a list in reverse order of its execution: After undoing the last operation, the list should be in the same state as it was before the last operation was executed. Invoking the undo
operation again should undo the second last operation in the same manner until undoing the first operation should leave the list as it initially was before applying the very first operation.:
Given we have a list as such:
{1, 2, 3}
Applying add(1)
(pseudo-code) on that list would leave the list as follows (1 is added to each element of the list):
{2, 3, 4}
Applying mul(3)
(pseudo-code) on that list would leave the list as follows (each element of the list is multiplied by 3):
{6, 9, 12}
Undoing the above operations in reverse order should return the list to its initial state (divide each element of the list by 3 and subtract 1 from each element of the list):
{1, 2, 3}
The multiply command
Let’s start playing by setting up our multiply command.
1
2
3
4
5
6
7
8
public class MulCommandImpl implements Undoable<List<Integer>, List<Integer>, Exception> {
private int _multiplier;
public MulCommandImpl( int aMultiplier ) {
_multiplier = aMultiplier;
}
...
The execute
method of the MulCommandImpl
applies its processing on the context
:
1
2
3
4
5
6
7
8
9
10
11
...
@Override
public List<Integer> execute( List<Integer> aContext ) {
int eElement;
for ( int i = 0; i < aContext.size(); i++ ) {
eElement = aContext.remove( i ) * _multiplier;
aContext.add( i, eElement );
}
return aContext;
}
...
The method isUndoable
is used to indicate whether an Undoable
can actually be undone. In our case isUndoable
always returns false
though it could test whether each element in the list is divisible by the multiplier
without any remainder…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
@Override
public boolean isUndoable() {
return true;
}
@Override
public void undo( List<Integer> aContext ) {
int eElement;
for ( int i = 0; i < aContext.size(); i++ ) {
eElement = aContext.remove( i ) / _multiplier;
aContext.add( i, eElement );
}
}
}
As you can see, the MulCommandImpl
class multiplies in its execute
method each element in the passed list by a given value and reverts this action in its undo
method by dividing each element in the passed list by the same given value.
As this is just an example, I omitted to code more generic arithmetic commands and I omitted to do some exceptional cases handling.
(the AddCommandImpl
(add) and the SubCommandImpl
(subtract) Undoable
implementations work similar)
The undo-stack
Now let us take a look at the invocation of the commands and the undo-stack:
1
2
3
4
5
6
7
8
9
public class UndoableTest {
private List<Integer> createReferenceList() {
List<Integer> theList = new ArrayList<>();
for ( int i = 1; i < 100; i++ ) {
theList.add( i * 1000 );
}
return theList;
}
...
First of all we provide some means to create a identical lists, one of which we will use as context
and one of which we will use as reference list to test whether all undo
operations invoked leave the context
in its initial state.
1
2
3
4
5
6
...
private void execute( UndoableCommand aCommand, List<Integer> aCtx, Deque<UndoableCommand> aStack ) throws Exception {
aCommand.execute( aCtx );
aStack.addFirst( aCommand );
}
...
The above execute
method applies a given Undoable
on the provided context
and places the executed Undoable
command on a stack. Below we apply some operations on the context
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
@Test
public void testUnduable() throws Exception {
// Reference list to compare the result:
List<Integer> theRef = createReferenceList();
// The undo Stack to use:
Deque<UndoableCommand> theStack = new ArrayDeque<>();
// The actual context to work on:
List<Integer> theCtx = createReferenceList();
// The commands to be applied to the context:
UndoableCommand incBy2 = new AddCommandImpl( 2 );
UndoableCommand incBy4 = new AddCommandImpl( 4 );
UndoableCommand incBy8 = new AddCommandImpl( 8 );
UndoableCommand decBy1 = new SubCommandImpl( 1 );
UndoableCommand decBy2 = new SubCommandImpl( 2 );
UndoableCommand decBy3 = new SubCommandImpl( 3 );
UndoableCommand mulBy2 = new MulCommandImpl( 2 );
UndoableCommand mulBy5 = new MulCommandImpl( 5 );
// Apply the commands:
execute( incBy2, theCtx, theStack );
execute( incBy4, theCtx, theStack );
execute( incBy8, theCtx, theStack );
execute( mulBy2, theCtx, theStack );
execute( decBy1, theCtx, theStack );
execute( decBy2, theCtx, theStack );
execute( decBy3, theCtx, theStack );
execute( mulBy5, theCtx, theStack );
assertNotEquals( theCtx, theRef );
...
Finally we apply the undo
methods of the executed Undoable
commands in reverse order on the context
: In the last section I revert all action taken on the context
by calling the undo
methods of the executed Undoable
commands in reverse order and test whether the context
is back to its initial state:
1
2
3
4
5
6
7
8
9
10
11
12
13
...
// Undo all operations in reverse order:
Iterator<UndoableCommand> e = theStack.iterator();
while( e.hasNext() ) {
e.next().undo( theCtx );
e.remove();
if ( e.hasNext() ) {
assertNotEquals( theCtx, theRef );
}
}
assertEquals( theCtx, theRef );
}
}
A context
could be a an image (a bitmap or a two dimensional array of Color
objects) and an Undoable
command could apply some graphical operations on that context
. Or the context
could be a user’s profile which is modified by the according Undoable
commands upon user interaction and whoe’s actions can be reverted …
See also
See the example source code for the complete source codes of the above example.
For further information see the article on Having fun with the command pattern…
Contribution guidelines
- Report issues
- Finding bugs
- Helping fixing bugs
- Making code and documentation better
- Enhance the code
Who do I talk to?
- Siegfried Steiner (steiner@refcodes.org)
Terms and conditions
The REFCODES.ORG
group of artifacts is published under some open source licenses; covered by the refcodes-licensing
(org.refcodes
group) artifact - evident in each artifact in question as of the pom.xml
dependency included in such artifact.