Google

Jun 25, 2014

What is the difference between strategy and command design patterns?

Many design patterns look similar, but there are subtle differences as I explained the differences between Proxy, Decorator, Adapter, Bridge, and Facade design patterns Why do Proxy, Decorator, Adapter, Bridge, and Facade design patterns look very similar? What are the differences? In this post, let's compare strategy and command patterns.

Q. What is the difference between a strategy and a command pattern?
A. Firstly, some example

Strategy - quicksort or mergesort, simple vs compound interest calculations, etc
Command - Open or Close actions, redo or undo actions, etc. You need to know the states undo.


Strategy:

public interface AbstractStrategy {
     abstract void execute(Object arg);
}


public class ConcreteStrategy implements AbstractStrategy {

    @Override
    public void execute(Object arg) {
        // Work with passed-in argument.
    }

}


Command:

public interface AbstractCommand {
     abstract void execute();
}


public class ConcreteCommand implements AbstractCommand {

    private Object arg;

    public ConcreteCommand(Object arg) {
        this.arg = arg;
    }

    @Override
    public void execute() {
        // Work with own state.
    }

}



Q. Can you spot the subtle differences?
A.
  1. Strategy handles how something should be done by taking the supplied arguments in the execute(....) method. For example simple and compound interest will be different classes like SimpleInterestStrategy and CompoundInterestStrategy with different algorithms. Command creates an object out of what needs to be done (i.e. hold state) so that these command objects can be passed around between other classes. The actions the command represent can be undone or redone by maintaining the state. There will tend to be a large number of distinct Command objects that pass through your application.
  2. Design patterns provide a common vocabulary among the designers and developers. So, by looking at the classes and interfaces suffixed with Strategy and Command, the designers and users will understand the intent (i.e. point 1)


Command design pattern example:

Here is an example where requests are batched and then either committed as a batch or if an exception is thrown, rolled back as a batch. 2 types of command objects are used to increment and decrement the count of number of requests added to a batch. Both these command objects maintain the count via the CountState object.

Step 1: The interface for the command objects.

public interface AbstractCommand {
    abstract void execute();
    abstract void undo();
}


Step 2: A POJO object to store count data.

public final class CountState {

  private int count;
  
  public void increment(){
   ++this.count;
  }
  
  public void decrement() {
   --this.count;
  }
  
  public int getCount() {
   return this.count;
  }
}


Step 3: Increment and Decrement command objects

public class IncrementCommand implements AbstractCommand {

 private CountState count;
 
 public IncrementCommand(CountState count) {
  this.count = count;
 }

 private String previousString;

 @Override
 public void execute() {
  count.increment();
  
 }

 @Override
 public void undo() {
  count.decrement();
 }
 
}


public class DecrementCommand implements AbstractCommand {

 private CountState count;
 
 public DecrementCommand(CountState count) {
  this.count = count;
 }

 private String previousString;

 @Override
 public void execute() {
  count.decrement();
  
 }

 @Override
 public void undo() {
  count.increment();
 }
 
}




Step 4: Now, the request processor class that is responsible for adding batching requests via addRequest() or removeRequest() methods and batch execution or rollback via commitBatch() and rollbackBatch() methods. This makes makes use of the command objects.

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;

public class RequestProcessor {

 private Deque<AbstractCommand> commandStack = null;
 private AbstractCommand incCmd;
 private AbstractCommand decCmd;

 private Map<String, String> requestCollection = new HashMap<>();

 public RequestProcessor(AbstractCommand incCmd, AbstractCommand decCmd) {
  this.commandStack = new ArrayDeque<>();
  this.incCmd = incCmd;
  this.decCmd = decCmd;
 }

 public void addRequest(String requestId, String requestTxt) {
  incCmd.execute();
  commandStack.push(incCmd);
  requestCollection.put(requestId, requestTxt);
 }

 public void removeRequest(String requestId) {
  decCmd.execute();
  commandStack.push(decCmd);
  requestCollection.remove(requestId);
 }

 public void commitBatch() {
  // ...logic to action all the requests
  // left out for brevity
  requestCollection.clear();
  this.commandStack = new ArrayDeque<>();
 }

 public void rollbackBatch() {
  AbstractCommand cmd = null;
  while (!commandStack.isEmpty()) {
   cmd = commandStack.pop();
   cmd.undo();
  }
 }
}


Finally, the client app that makes use of the RequestProcessor and other objects.

public class ClientApp {

 public static void main(String[] args) {
  CountState state = new CountState();
  AbstractCommand incCmd = new IncrementCommand(state);
  AbstractCommand decCmd = new DecrementCommand(state);
  RequestProcessor processor = new RequestProcessor(incCmd, decCmd);

  processor.addRequest("1", "productcode:123, price:25.50");
  processor.addRequest("2", "productcode:456, price:12.50");
  processor.addRequest("3", "productcode:789, price:14.50");

  // ...

  processor.removeRequest("3");

  try {

   processor.commitBatch();
   System.out.println("number of requests processed:" + state.getCount()); // 2
   throw new RuntimeException();

  } catch (Exception ex) {
   processor.rollbackBatch();
   System.out.println("number of requests processed:" + state.getCount()); // 0
  }
 }
}


The strategy deign pattern example is covered in Java design pattern interview questions and answers: strategy and factory pattern

Labels: ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home