Skip to end of metadata
Go to start of metadata

Introduction

The "simple command framework" (SCF) is an easy and efficient method to write software that fits the SOLID principles. Mainly the Open-Close-Principle is implemented in an easy art and wise. The different steps of a process or  functionality will be encapsulated in so called command objects. The different command objects will be processed in a defined order. This framework follows the Command Design Pattern.

The framework uses functionality of Java 8 meanly default implementation of interfaces and in some minor cases lamda expressions.

The Idea of the framework

A Command object is a java class which provides an execute() method to proceed a piece of implementation of a business logic. Usually the business logic isn't implemented in the command itself but in a service layer which provides business logic.

If you use a dependency injection container like spring you can use spring IoC to inject the service classes you need. You can use spring as well to couple different commands to a chain via spring configuration.

The simple command framework has three modes so far:

  1. Create a command chain and execute it. The execution is done either in the same order the commands are inserted in the so called command container or they can be executed by a certain order (priority). The aim of this case is to execute functionality which is straight forward. In this case the execute() method of the command container should be called. To use this behavior you implement the Command interface.
  2. The command chain can also be executed as the chain of responsibility pattern. For this every command decides if the problem is solved or if another chain should be called. In this case the executeAsChain() method should be called. To use this behavoir you extend the AbstractDefaultChainCommand class.
  3. The command chain can be executed as a process as well. In this case the executed command object has to be decided which actual command should be called next. In this case the executeAsProcess() method should be called on the command container. To use this behavior you extend the AbstractDefaultProcessCommand class.

Along the chain or process you can bypass a parameter object to share data among this chain. If you don't need a parameter object you can pass either null or DefaultParameterObject.NULLCONTEXT to mark explicitly, that no parameter information is needed where DefaultParameterObject.NULLCONTEXT is actually a DefaultParamterObject implementation.

The simple command framework comes with a couple of examples to show how to handle everything.

The framework is hosted on github. Check out on https://github.com/simplecommand. It is 100% test driven developed, PI-tested and has no technical debt (ideally). Feel free to write tests which are not passed or contribute your own code. Contact simplecommand@manfred-wolff.de. The framework is free software under the conditions of GNU Public Lesser License.

Why another framework

Old ideas – divide and conquer.


Cutting software in little pieces is an old idea. There are design patterns as command-, chain of responsibility- or strategy pattern which follows the same idea. What is so important on these frameworks?

  • A complex problem is split into easy or complicated problem. Each piece does a simple thing.
  • The simple little peace can be tested easily.
  • You can provide a parameter object passing around all pieces to get some glue to fit them together.

The simple Idea is to split each call in a method into an object (encapsulating algorithms in objects). There are several other frameworks which offers an implementation of these problems but they are rather old so I just started to implement my own one. What is the difference: It is 100% test driven developed and has code coverage as well as a mutation coverage of 100%.

The Architecture of the simple command framework

The architecture of the simple command framework is - as named - simple. It comes with a couple of interfaces and some default implementation.

For each purpose you've a dedicated interface and sometimes an abstract implementation. You can pass a parameter object among the whole chain which has to fulfil the ParameterObject interface. The ParameterObject interface is just a marker for those objects.

The framework follows the composite pattern. You've a second interface which is a command as well but works as a container of commands. You can create chains with commands and command container as well.

For each interfaces you've default implementations which you can use for your own purposes.

The AbstractDefaultChainCommand provides an empty execute() method but an implementation of the chain of responsibility framework. The executeAsChain() method calls the execute() method. If there is no exception thrown it returns true. True means, the work hasn't been done and the next command should be called. If the execute() method throws an exception, it is an indicator that the execution of the chain should be stopped. For this the executeAsChain() method results false.

This is a good approach if the execution of the chain has to be stopped because an error has occurred. It may be a bad approach, if you want to stop because everything is fine. In this case you've to throw an exception: Everything is fine, the work is done. This is a little bit an anti-pattern but unfortunately there is no "positive exception" in Java.

The DefaultCommandContainer has convenient methods to add commands to a  container. Either the natural order (just add it) or via priority. If you add two commands with the same priority the first inserted command is executed first. Even the container has the same behavior as a simple command you can execute the container in the same way as a command. You can add a container in a container as well.

For bypassing parameter objects you may want to use the GenericParameterObject interface, which provides methods to add key-value pairs into the object. An implementation of that is the DefaultParameterObject.

Again a brief description of the parts of the framework

Command: Interface for wrapping an algorithm. Everything is a command.

ChainCommand: Interface for a command which works in the second mode: Chain of Responsitibility.

ProcessCommand: Interface for a command which works in the third mode: Process Mode.

CommandContainer: Interface for grouping different Command objects in a container. This fits the composite pattern: CommandContainer objects have the same behavior as Command objects. For this it is possible to add Command objects as well as CommandContainer in such a container.

ParameterObject: Marker interface for a parameter object. ParameterObject objects are the glue between different commands. Because of the concept of these objects the whole framework is thread-safe and supports re-entrant algorithms.

GenericParameterObject: Interface for a generic parameter object that holds a list of keys and values. A better approach is to create own type-safe parameter objects with get- and set-methods for the values (Bean convention). Because the whole framework works with generics you can instantiate each command with your own parameter object.

AbstractDefaultChainCommand: An implementation which provides a generic method for fulfilling the chain-of-responsibility pattern. Just call executeAsChain(). All commands will be executes. If one command fails the execution is stopped.

AbstractDefaultProcessCommand: An Implementation which provides a generic method for fulfilling the process mode. Just call executeAsProcess(). All commands will be executed in the right way.

DefaultCommandContainer: An implementation to add commands into a container. The execution of the container will execute all parts of it (either commands or command-containers). This fulfils the composite pattern. The DefaultCommandContainer actually implements all three modes.

DefaultParameterObject: An implementation of the GenericParameterObject interface to work with generic data in a generic data structure. This is not recommended because it is not type safe.

ChainBuilder: The Chainbilder ist an interface to build chains from different sources e.g. XML, java configured or any other. Concrete classes have to implement the buildChain() method.

InjectionChainBuilder: Chainbuilder which takes a spring configuration or any other DI framework to build the actual chain.

XMLChainBuilder: Chainbuilder which takes an XML configuration to build the actual chain.

Working with commands

Handling single commands

You can use SCF to handle single commands. You can encapsulate algorithm or business logic or glue code in commands and just execute them. A good use case is to delegate methods to commands. If you use a dependency injection framework, you can inject certain implementations of a command. This makes your software maximum flexible.

Behavior of a single command

The behavior of a single command is as follow:

  • The whole business code is hold in the execute() method. This is true for all three modes.
  • The executeAsChain() method calls the execute() method to fulfil the business logic. After executing the method decides if the given problem is solved already (return false) or if a possible next command in the chain should overtake (return true). In other words: The executeAsChain() method does nothing more than this decision. The AbstractDefaultCommand implements this method in a right way.
  • The executeAsProcess() method calls the execute() method to fulfil the business logic quite in the same way as executeAsChain() does. But after executing the method decides explicit which process step should overtake (return a processID) or if the execution should be stopped (return null). Use the DefaultCommandContainer to execute processes.

Example for a single command

Just have a look as this phantasy service called CheckoutService. The aim of this service is doing all stuff which is necessary during a eCommerce checkout process.

The service is ready to use with a dependency injection framework using setter injection. The business method addAdress() (line 23) does nothing than delegation the call to a command. The actual implementation can be configured outside the class. So actually if you want to change the implementation you don't have to inhire from the service providing "your service". You can just switch the injection of the addAdressExecuter to another implementation. So you are as much as flexible you can be. If you want to enhance this object with other parameters no problem. ParameterObject is just an interface.

The usage is shown in the test of this service

Handling commands in chains

First you've to decide, which approach you want to have.

The first mode is a continuous working of the commands step by step. The CommandContainer executes each command. Exceptions are not handled. That means: If a command throws an exception the execution of other commands will not stop. Example: If you've five evaluations for a password. Each evaluator is encapsulated in a command - of course. So actually you don't want to stop evaluation if one evaluator fails because you want to provide all problems to your caller, not even one. So each evaluator writes an error message in the parameter object and you can provide all messages to the caller.

The second mode is a continuous working as well. You can configure what should happen if a command throws an exception. In this mode - the CoR mode (chain of responsibility mode) - an exception is handled automatically and each command can decide if the execution of the whole chain should be stopped (throw an exception) or if the next command should overtake. By default the chains stops if even one command throws an exception.

The third mode acts as the second mode but the container just executes the processID which was given from the last executed command. With this mechanism you are able to manage whole business processes where you can have commands which work as actors and other commands which work as decisions. Because a CommandContainer is a Command as well you can manage sub processes as well.

Coupling commands together

The CommandContainer interface provides a concept to couple commands to a chain or a process. The interface provides three methods: One to couple commands via order, one to couple via priority and one to couple via name.

If you want to be sure, that commands act in a actual order you've to add the commands via the method

void addCommand(int priority, Command<T> command);

and ensure, that each priority is configured exactly one time. If the order of the command doesn't matter you can take the

void addCommand(Command<T> command);

method to couple commands to a chain. For example: You've a password policy which contains five different methods to validate a password (has to be one digit, one capital letter, more than 10 characters, one special character ...) the order of the commands to validate a password is not necessary.

Because of the composite pattern you can add commands, command containers or both together in a CommandContainer.

Working with chain builder

The framework offers different chain builder namely the InjectionChainBuilder and the XMLChainBuilder. A builder should fulfil the ChainBuilder interface:

As you see the ChainBuilder itself is a Command so you can execute the builder in the same way as a Command and a builder can be inserted into a CommandContainer as well.

Configuring via Spring

The InjectionChainBuilder fulfils the ChainBuilder interface. In additional it offers a method for dependency setter injection. The 

setCommands(final List<Command<T>> commands)

method requires a list of commands which you can configure wire spring.

Configuring via XML

It is possible to configure commands via XML.

For execute this xml the XMLChainBuilder is used.

Instead setting a list of commands in this case an XML file name is set. The file has to be in the class path.

Example for the process mode

First of all: Proceses can only be configured via XML. In the current version it is not possible to configure it via Spring.

For processes we've an advanced xml notation explained here:

The xsd for this file is located here:

 Expand source

The process can be startet as stated in the next Test:

The commands has to return one of the transition returnvalues.

Process in Practice

Lets look on this simple process:

Each node becomes a ProcessCommand object and each transition a Transition object. The following steps are necasary:

  1. We've to write the xml file for the process definition
  2. Implement the ParameterObject
  3. For each step we've to write a test and implement the step.
  4. We've to write an integration test for both pathes.

The complete source code for this example you can get here: https://github.com/simplecommand/SimpleCommandFramework-Examples/tree/master/process-demo

Writing the process definition

Writing  the ParameterObject

For POJOs I typically write no test. Either I generate it with an generator or I generate get-/set-Methods via Eclipse. They will be tested transitiv via the business logic around. If a get-/set-operation is not testet typically it is not used and can be deleted.

Writing tests for each Command

We just show on one command, how you can test the ProcessCommand without the XML Configuration just to test:

The Implementation of our StartCommand is very simple:

Writing the integration test

Working with a DI container - Spring

Spring is just one example how to use everything with a DI container. You will see: There is not so much code left.

We use the same example as above. First we've to change the test.

As you see above now the checkout service is injected by spring. We've just to provide the right annotation and of course we've mark the service as component. In additional we need a configuration of the spring framework. In our case we use java configuration.

First the implementation of the service.

You see as well that the command is injected into the service too. For this the command has to be marked as component too.

Finally we need a spring configuration.

That's it.

The maven configuration of the project.

Maven pom.xml  Expand source

 

Refactor legacy code to SCF

Refactoring to SRP

The open-close principle is one part of SOLID. Another part is the single responsibility principle. “There should only one reason to change a class (method).” So the first step is a refactoring to the SRP clean code principle.

One level of abstraction

Try to refactor the legacy code to two levels of abstraction: Public methods have no “calculation” just calling methods. All private methods hold algorithms that are called from the public method. One level of abstraction means that each method should follow only one of these two levels I described.

So actually if you've a logic which has different steps you should refactor it in a way:

Refactoring to pattern

After the software is prepared the refactoring is very easy:

  • Change the signature of all private algorithms to parameter objects.
  • Encapsulate each algorithm into a Command-object.
  • Change the public method: Just add all algorithms into a CommandContainer.
  • Prepare the parameter object.
  • Execute the CommandContainer bypassing the parameter object either brut forced or via chaining.
  • Evaluate the parameter object.
  • (optional) Provide a configuration of your container e.g. with the Spring framework or via the Builder-Pattern.

Now each Command takes control of the whole execution. It can read and write values into the parameter object and can decide even the execution should going on or should stop.

 
  • No labels