Command Pattern

Definition

The Command Pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queues or log requests, and support undoable operations.

image

Figure 01. Class diagram of the Command Pattern. Ref: http://en.wikipedia.org/wiki/Command_pattern

As can be seen in the previous figure, the elements that characterize the Command Pattern are:

  • An invoker
  • A command
  • A receiver

The invoker is the one that will execute any Command that is set to it.

The command is the interface that will have the generic methods –commands- that will be specifically implemented by the classes that extend it.

The receiver is the class that will actually have all the logic required to execute any of the commands invoked by the invoker.

As we can see here, we are generating actual objects (ConcreteCommand objects) that will represent the commands that need to be executed by the receiver, as the definition of this pattern states.

Scenario

We need to implement a system that will either submit or update orders.

The solution proposed can be seen in the following class diagram:

image

Figure 02. Class diagram of the solution proposed for the study case.

In this diagram, these are our Command Pattern elements:

  • Invoker: OrderAgent class
  • Command: OrderCommand interface
  • Receiver: OrderManager class

The OrderAgent class has the following code:

OrderAgent.java

package chris.desingpatterns.command; import chris.desingpatterns.command.commmands.OrderCommand; public class OrderAgent { private OrderCommand command; /** * Method that will store all the * orders places by an Agent. * * @param command */ public void sendCommand(OrderCommand command){ this.command = command; command.excecute(); } public void undoCommand() { if(command != null){ command.undo(); } } }

As we can see here the agent will be capable of execute to actions –commands-:

  • sendCommand() to execute the command.
  • undoCommand() to revert the command executed.

The command interface looks like this:

OrderCommand.java

package chris.desingpatterns.command.commmands; public interface OrderCommand { /** * This is a general method that will * execute an action in a Receiver, * in this case the OrderManager class. */ void excecute(); /** * Method that will revert an action * that could be processed at the end. * */ void undo(); }

One of the concrete commands is:

UpdateOrder.java

package chris.desingpatterns.command.commmands; import chris.desingpatterns.command.receivers.OrderManager; public class UpdateOrder implements OrderCommand { private OrderManager orderManager; public UpdateOrder(OrderManager orderManager) { this.orderManager = orderManager; } /** * {@inheritDoc} */ public void excecute() { orderManager.update(); } /** * {@inheritDoc} * */ public void undo() { orderManager.restoreOriginal(); } }

And the class that have all the logic to execute all the actions required by the invoker is:

OrderManager.java

package chris.desingpatterns.command.receivers; import java.util.List; import org.apache.log4j.Logger; import chris.desingpatterns.command.dao.OrderDao; import chris.desingpatterns.command.pojos.Order; import chris.desingpatterns.command.pojos.OrderFactory; import chris.desingpatterns.command.pojos.Product; public class OrderManager { private static Logger log = Logger.getLogger(OrderManager.class); private OrderDao dao; private OrderFactory orderFactory; private List<Product> products; private Order orderFound; private Order currentOrder; public OrderManager() { dao = OrderDao.getInstance(); orderFactory = OrderFactory.getInstance(); } /** * Method that will replace an existing order * by a new one but that will preserve the same * ID as the old order. * */ public void update() { if(orderFound != null){ String oldOrderId = orderFound.getId(); orderFactory.setProducts(products); Order newOrder = orderFactory.createOrder(); newOrder.setId(oldOrderId); currentOrder = newOrder; dao.update(oldOrderId, newOrder); }else{ log.warn("Order not found"); } } /** * Method that will create a new Order * that will be saved by the DAO. * */ public void submit() { orderFactory.setProducts(products); Order newOrder = orderFactory.createOrder(); currentOrder = newOrder; dao.store(newOrder); } /** * Method that will remove an order * that was saved by the DAO. * */ public void remove() { String id = currentOrder.getId(); dao.delete(id); } /** * Method that will get an order * that was cached. * */ public void restoreOriginal() { String id = currentOrder.getId(); currentOrder = dao.getCached(id); } /** * Method that will retrieve an order * that was saved by the DAO. * * @param id */ public void findOrder(String id) { orderFound = dao.get(id); } public void setProducts(List<Product> products) { this.products = products; } public Order getCurrentOrder() { return currentOrder; } }

And the class that will run or study case (in Command Pattern terms: the Client) is:

Main.java

package chris.desingpatterns.command; import java.util.ArrayList; import java.util.List; import chris.desingpatterns.command.commmands.OrderCommand; import chris.desingpatterns.command.commmands.SubmitOrder; import chris.desingpatterns.command.commmands.UpdateOrder; import chris.desingpatterns.command.pojos.Order; import chris.desingpatterns.command.pojos.Product; import chris.desingpatterns.command.receivers.OrderManager; public class Main { /** * @param args */ public static void main(String[] args) { /********* PRODUCTS *************/ Product tv = new Product(); tv.setDescription("TV Sony"); tv.setName("TV"); Product radio = new Product(); radio.setDescription("Radio Panasonic"); radio.setName("RADIO"); Product telephone = new Product(); telephone.setDescription("iPhone Phone"); telephone.setName("TELEPHONE"); /*********************************/ OrderAgent agent = new OrderAgent(); List<Product> productsA = new ArrayList<Product>(); productsA.add(tv); productsA.add(radio); OrderManager orderManagerA = new OrderManager(); orderManagerA.setProducts(productsA); // SUBMIT ORDER 1 System.out.println(".:.SUBMIT ORDER 1.:."); OrderCommand submitCommand = new SubmitOrder(orderManagerA); agent.sendCommand(submitCommand); print(orderManagerA.getCurrentOrder()); //REVERT ORDER SUBMITED System.out.println(".:.UNDO SUBMIT ORDER 1.:."); agent.undoCommand(); //New Order to Submit List<Product> productsB = new ArrayList<Product>(); productsB.add(telephone); productsB.add(tv); productsB.add(radio); OrderManager orderManagerB = new OrderManager(); orderManagerB.setProducts(productsB); // Submit ORDER 2 System.out.println(".:.SUBMIT ORDER 2.:."); submitCommand = new SubmitOrder(orderManagerB); agent.sendCommand(submitCommand); print(orderManagerB.getCurrentOrder()); String id = "1"; OrderManager orderManagerC = new OrderManager(); orderManagerC.findOrder(id); List<Product> productsC1 = new ArrayList<Product>(); productsC1.add(telephone); orderManagerC.setProducts(productsC1); //Update ORDER 1 System.out.println(".:.UPDATE ORDER 1.:."); OrderCommand updateCommand = new UpdateOrder(orderManagerC); agent.sendCommand(updateCommand); print(orderManagerC.getCurrentOrder()); //Submit ORDER 3 System.out.println(".:.SUBMIT ORDER 3.:."); OrderManager orderManagerD = new OrderManager(); List<Product> productsD = new ArrayList<Product>(); productsD.add(radio); productsD.add(telephone); orderManagerD.setProducts(productsD); submitCommand = new SubmitOrder(orderManagerD); agent.sendCommand(submitCommand); print(orderManagerD.getCurrentOrder()); //Update ORDER 3 System.out.println(".:.UPDATE ORDER 3.:."); OrderManager orderManagerE = new OrderManager(); List<Product> productsE = new ArrayList<Product>(); productsE.add(telephone); productsE.add(radio); productsE.add(tv); orderManagerE.setProducts(productsE ); orderManagerE.findOrder("3"); updateCommand = new UpdateOrder(orderManagerE); agent.sendCommand(updateCommand); print(orderManagerE.getCurrentOrder()); //UNDO Update ORDER 3 System.out.println(".:.UNDO UPDATE ORDER 3.:."); agent.undoCommand(); print(orderManagerE.getCurrentOrder()); } private static void print(Order order){ if(order != null){ System.out.println("----------------------------"); System.out.println("Products for Order["+order.getId()+"]: " ); for(Product p : order.getProducts()){ System.out.println("- " + p.getName()); } System.out.println("----------------------------"); } } }

That is it!

To download the code click in the following link:

Command Pattern

Do you have any recommendations or amendments to be done, please let me know to improve this post!