Gang of Four Behavioural pattern: Command
Behavioural Pattern
Encapsulate a command request as an object. Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
The Command pattern can be seen in web frameworks like the Struts framework:
In the Struts framework different urls are mapped to different Commands. A centralised Controller called the Dispatcher Controller delegates to a particular Command object to do some processing when it receives a particular url. After the Command has finished processing the request and attributes are forwarded to a JSP servlet which merges HTML and the attributes into the response.
To get the code for this example:
git clone https://github.com/spotadev/gangoffour.git
In src/main/java navigate to this package:
com.javaspeak.designpatterns.go4.behavioural.command
You can run the code from the main method of:
CommandApplication
This example shows similarity to the Struts framework:
A call is made to the processRequest method of the RequestProcessor passing it a map of attributes.
The RequestProcessor retrieves the RequestType from the request and executes a command which is mapped to that RequestType.
The Command class reads the map of attributes and creates a modified new map of the attributes which it calls the handleResponse(..) method of the Response with. The handleResponse method prints the modified attributes to System.out.
This example calls the RequestProcessor twice, each time with a different RequestType.
The first RequestType is RequestType.CAPITALISE and the RequestProcessor calls the CapitaliseCommand.
The second RequestType is RequestType.SORT and the requestProcessor calls the SortCommand
Both the calls to the RequestProcessor use the same ResponseHandler which prints the following to System.out:
===============================================
message : HELLO WORLD!
quote : COMMON SENSE IS NOT COMMON
name : JOHN DICKERSON
===============================================
quote : Common Sense is not Common
message : Hello World!
name : John Dickerson
===============================================
Notice that the first command capitalised the text and the second command sorted the attribute keys by the attribute values.
package com.javaspeak.designpatterns.go4.behavioural.command;
import java.util.HashMap;
import java.util.Map;
/**
* Text book description:
* <ul>
* Command: Encapsulate a command request as an object. Encapsulate a request as an object,
* thereby letting you parameterize clients with different requests, queue or log requests,
* and support undoable operations.
* </ul>
* The Command pattern can be seen in web frameworks like the Struts framework.
* <p>
* In the Struts framework different urls are mapped to different Commands. A centralised
* Controller called the Dispatcher Controller delegates to a particular Command object to do
* some processing when it receives a particular url. After the Command has finished processing
* the request and attributes are forwarded to a JSP servlet which merges HTML and the attributes
* into the response.
* <p>
* This example shows similarity to the Struts framework:
* <p>
* A call is made to the processRequest method of the RequestProcessor passing it a map of
* attributes.
* <p>
* The RequestProcessor retrieves the RequestType from the request and executes a command which is
* mapped to that RequestType.
* <p>
* The Command class reads the map of attributes and creates a modified new map of the attributes
* which it calls the handleResponse(..) method of the Response with. The handleResponse
* method prints the modified attributes to System.out.
* <p>
* This example calls the RequestProcessor twice, each time with a different RequestType.
* <p>
* The first RequestType is RequestType.CAPITALISE and the RequestProcessor calls the
* CapitaliseCommand.
* <p>
* The second RequestType is RequestType.SORT and the requestProcessor calls the SortCommand
* <p>
* Both the calls to the RequestProcessor use the same ResponseHandler which prints the
* following to System.out:
* <ul>
* ===============================================<br/>
* message : HELLO WORLD!<br/>
* quote : COMMON SENSE IS NOT COMMON<br/>
* name : JOHN DICKERSON<br/>
* ===============================================<br/>
* quote : Common Sense is not Common<br/>
* message : Hello World!<br/>
* name : John Dickerson<br/>
* ===============================================<br/>
* </ul>
* Notice that the first command capitalised the text and the second command sorted the attribute
* keys by the attribute values.
* <p>
* @author John Dickerson - 21 Feb 2020
*/
public class CommandApplication {
private Response response =
new Response() {
@Override
public void handleResponse(
Map<String, String> responseAttributes ) {
for ( String key : responseAttributes.keySet() ) {
System.out.println(
key + " : " + responseAttributes.get( key ) );
}
}
};
public void runExample() {
RequestProcessor requestProcessor = new RequestProcessorImpl();
Long requestId = 1L;
Map<String, String> requestAttributes = new HashMap<String, String>();
requestAttributes.put( "name", "John Dickerson" );
requestAttributes.put( "message", "Hello World!" );
requestAttributes.put( "quote", "Common Sense is not Common" );
Request request = new RequestImpl( requestId, RequestType.CAPITALISE, requestAttributes );
System.out.println( "===============================================" );
requestProcessor.processRequest( request, response );
request = new RequestImpl( requestId, RequestType.SORT, requestAttributes );
System.out.println( "===============================================" );
requestProcessor.processRequest( request, response );
System.out.println( "===============================================" );
}
public static void main( String args[] ) {
CommandApplication application = new CommandApplication();
application.runExample();
}
}
package com.javaspeak.designpatterns.go4.behavioural.command;
/**
* The application calling code calls the processRequest(..) method of RequestProcessor. The
* implementation of processRequest retrieves the RequestType from the request and from the
* RequestType gets the Command that is mapped to it. It then calls the execute method of the
* Command.
* <p>
* The execute method of Command in turn reads a map of attributes from the request; processes
* them and creates a new map which is passes as an argument to the callback method
* handleResponse(..) of the response.
*
* @author John Dickerson - 21 Feb 2020
*/
public interface RequestProcessor {
/**
* Delegates the request and response to a Command. The request has a RequestType which
* holds a reference to the Command.
*
* @param request
* The request holding a map of attributes and a RequestType which references the Command
* to execute.
*
* @param response
* The response which has a call back method called handleResponse(..) which a new map
* of processed attributes can be passed to.
*/
public void processRequest( Request request, Response response );
}
package com.javaspeak.designpatterns.go4.behavioural.command;
/**
* The application calling code calls the processRequest(..) method of RequestProcessor. The
* implementation of processRequest retrieves the RequestType from the request and from the
* RequestType gets the Command that is mapped to it. It then calls the execute method of the
* Command.
* <p>
* The execute method of Command in turn reads a map of attributes from the request; processes
* them and creates a new map which is passes as an argument to the callback method
* handleResponse(..) of the response.
*
* @author John Dickerson - 21 Feb 2020
*/
public class RequestProcessorImpl implements RequestProcessor {
@Override
public void processRequest( Request request, Response response ) {
RequestType requestType = request.getRequestType();
requestType.getCommand().execute( request, response );
}
}
package com.javaspeak.designpatterns.go4.behavioural.command;
import java.util.Map;
/**
* The RequestProcessor calls the execute(..) method of Command passing in the request and
* response to it.
* <p>
* The Command to call the execute() method on is retrieved from the RequestType stored in the
* request.
*
* @author John Dickerson - 21 Feb 2020
*/
public interface Request {
/**
* The requestId provides a unique identify for the request. The current implementation does
* not use it.
*
* @return requestId
* The requestId unique identifies the request
*/
public Long getRequestId();
/**
* The RequestType holds the mapping to a Command
*
* The RequestProcessor uses the RequestType from a Request to retrieve the Command to call
* execute(..) on.
*
* @return RequestType
* RequestType holds the mapping to a Command
*/
public RequestType getRequestType();
/**
* Retrieves a map of Request attributes which are String key value pairs.
* <p>
* The idea is that the execute(..) method of Command will read the request attributes; process
* them and then return them in a new map via the callback method handleResponse(..) in the
* response argument of the execute(..) method
*
* @return Map
* Map of modified attributes
*/
public Map<String, String> getRequestAttributes();
}
package com.javaspeak.designpatterns.go4.behavioural.command;
import java.util.Map;
/**
* The RequestProcessor calls the execute(..) method of Command passing in the request and
* response to it.
* <p>
* The Command to call the execute() method on is retrieved from the RequestType stored in the
* request.
*
* @author John Dickerson - 21 Feb 2020
*/
public class RequestImpl implements Request {
private Long requestId;
private RequestType requestType;
private Map<String, String> requestAttributes;
/**
* Constructor
*
* @param requestId
* The requestId provides a unique identify for the request. The current implementation
* does not use it.
*
* @param requestType
* The RequestType holds the mapping to a Command. The RequestProcessor uses the
* RequestType from a Request to choose the Command to call execute(..) on.
*
* @param requestAttributes
* Map of modified attributes. The idea is that the execute(..) method of Command
* will read the request attributes; process them and then return them in a new map
* via the callback method handleResponse(..) in the response argument of the execute(..)
* method
*/
public RequestImpl(
Long requestId, RequestType requestType,
Map<String, String> requestAttributes ) {
this.requestId = requestId;
this.requestType = requestType;
this.requestAttributes = requestAttributes;
}
@Override
public Long getRequestId() {
return requestId;
}
@Override
public RequestType getRequestType() {
return requestType;
}
@Override
public Map<String, String> getRequestAttributes() {
return requestAttributes;
}
}
package com.javaspeak.designpatterns.go4.behavioural.command;
import java.util.Map;
/**
* The Response is passed to the RequestProcessor's processRequest(..) method along with the
* Request. The RequestProcessor in turn retrieves the RequestType from the Request and from the
* RequestType retrieves the Command. The Request and Response is then passed to the Command's
* execute method.
*
* @author John Dickerson - 21 Feb 2020
*/
public interface Response {
/**
* The handleResponse(..) method is a callback method that can be called by the RequestProcessor
* when it has completed processing the map of attributes retrieved from the Request.
* The handleResponse(..) method has a new map of response attributes passed to it.
*
* @param responseAttributes A map of response attributes
*/
public void handleResponse( Map<String, String> responseAttributes );
}
package com.javaspeak.designpatterns.go4.behavioural.command;
/**
* Specifies the RequestType. The RequestType can be retrieved from the Request by the
* RequestProcessor. Inside the RequestType is a reference to the command to execute.
*
* @author John Dickerson - 21 Feb 2020
*/
public enum RequestType {
// capitalises all the attributes
CAPITALISE( new CapitaliseCommand() ),
// sorts the keys in the map by the values
SORT( new SortCommand() );
// holds a reference to the command associated with this enum. As the
// reference is in an Enum the reference is a singleton.
private Command command;
/**
* Private constructor of enum
*
* @param command the command to execute
*/
private RequestType( Command command ) {
this.command = command;
}
public Command getCommand() {
return this.command;
}
}
package com.javaspeak.designpatterns.go4.behavioural.command;
/**
* All Commands need to implement this interface
*
* @author John Dickerson - 21 Feb 2020
*/
public interface Command {
/**
* The implementation of this method should retrieve a Map of request attributes from the
* request. It should then modify them and add them to a new map and call the
* handleResponse( Map<String,String> responseAttributes ) method to return them to the
* calling code.
*
* @param request
* Request which has the map of attributes to process
*
* @param response
* Response which has the call back method handleResponse(..) to call
*/
public void execute( Request request, Response response );
}
package com.javaspeak.designpatterns.go4.behavioural.command;
import java.util.HashMap;
import java.util.Map;
/**
* This Command class reads in a map of request attributes from the request and for each of the
* request attributes capitalises the value and returns them in a new map which is passed to the
* handleResponse(..) method.
*
* @author John Dickerson - 21 Feb 2020
*/
public class CapitaliseCommand implements Command {
@Override
public void execute( Request request, Response response ) {
Map<String, String> requestAttributes = request.getRequestAttributes();
Map<String, String> responseAttributes = new HashMap<String, String>();
for ( String key : requestAttributes.keySet() ) {
responseAttributes.put( key,
requestAttributes.get( key ).toUpperCase() );
}
response.handleResponse( responseAttributes );
}
}
package com.javaspeak.designpatterns.go4.behavioural.command;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
/**
* This Command class reads in a map of request attributes from the request and sorts them by the
* attribute values and returns them in a new map which is passed to the handleResponse(..) method.
*
* @author John Dickerson - 21 Feb 2020
*/
public class SortCommand implements Command {
/**
* A comparator that sorts the keys of the map according the values
* associated with each key
*/
class ValueComparator implements Comparator<String> {
private Map<String, String> base;
public ValueComparator( Map<String, String> base ) {
this.base = base;
}
public int compare( String a, String b ) {
int compare = base.get( a ).compareTo( base.get( b ) );
if ( compare == 0 ) {
compare = 1;
}
return compare;
}
}
@Override
public void execute( Request request, Response response ) {
Map<String, String> requestAttributes = request.getRequestAttributes();
Map<String, String> responseAttributes =
new TreeMap<String, String>(
new ValueComparator( requestAttributes ) );
responseAttributes.putAll( requestAttributes );
response.handleResponse( responseAttributes );
}
}
Back: Gang of Four
Page Author: JD