Monday, June 2, 2008

Strategy Pattern

Strategy is one of the elegant design patterns which uses the polymorphism feature of object oriented language. This pattern allows you to change the strategy of how the class will behave at run time, so that your code can keep up with change.

Why "Strategy" :

  1. Can change how the class will behave at run time
  2. Makes your code reusable and extendable

First let's make a simple scenario that is appropriate for us to use Strategy Pattern. Suppose you need to write a simple copy function that copies a file from one place to another. The problem is that your boss wants to support different OS platform. You can say why need strategy I can write a simple code like,

public class FileCopier {

   public void copyFile(File source, File destination) {
       if (platform == windows) {
           copyWindowFile(source, destination);
       } else if (platform == linux) {
           copyLinuxFile(source, destination);
       }
   }

   public void copyWindowFile(File source, File destination) {
       //here is your windows copy code 
   }

   public void copyLinuxFile(File source, File destination) {
       //here is your Linux copy code 
   }
}

Now what if your boss tells you to add support for Mac OS. Then you will have to change the FileCopier class and re-test it. This surely will not make your boss happy as FileCopier is a very important class and people don't want it to change. One other thing is that change in FileCopier class to add support for Mac will also impact the software for other platforms, which is not desired.

One of the key feature of using real good object orientation is that it reduces change. And adding endless if-else block will make your code lengthy and fragile and you will also face the wrath of the other programmer who might have to change your code.

How to use "Strategy" :

Class Diagram:

Strategy

Java Code:

IFileCopier.java:
public interface IFileCopier {
   void copyFile(File source, File destination);
}
WindowsFileCopier.java
public class WindowsFileCopier implements IFileCopier {

   public void copyFile(File source, File destination) {
       //copy file in Window machine
   }
}
LinuxFileCopier.java
public class LinuxFileCopier implements IFileCopier {

   public void copyFile(File source, File destination) {
       //copy file in Linux machine
   }
}

When IFileCopier is used in any context, it is coded using the interface IFileCopier. So it works for all the implementations of IFileCopier. When you will pass an instance of LinuxFileCopier it will have the behavior of copying file in Linux and an instance of WindowsFileCopier it will have the behavior of copying file in Windows. Now you can create another implementation of IFileCopier and pass the new implementation. As long as your implementation is OK the existing code will be OK : ). So here you are actually reducing the amount of change in existing code. So the amount of test is reduced and you can cope up with the change faster.

Object Oriented Principals used in Strategy :

1. Code to interface or abstraction not to concrete class. 2. High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.[Dependency Inversion Principal] 3. Single Responsibility Principal(SRP) 4. Encapsulate what varies from what stays the same 

We see the first principal in Context class as there we did code to interface(IFileCopier). Context is high level class and WindowsFileCopier or LinuxFileCopier is lower level class as Context uses them. But Context is actually dependent on an interface not to a concretion. So Strategy follows Dependency Inversion Principal. Single Responsibility Principal states that, "a class should have only one reason to change". In other words a class should do one thing and do it very well. Here we used this principal as we separated the OS dependant logic to separate class. So change in one part will not effect change in other parts. One other important thing is that we should identify parts that varies in a system. So that we could encapsulate what varies from what stays the same. Here we encapsulated the behavior to copy files, so that it could give us flexibility to re use it to support different OS platform. This will allow us to minimize change and maximize flexibility.

References:

http://sourcemaking.com/design_patterns/strategy http://www.exciton.cs.rice.edu/JavaResources/DesignPatterns/StrategyPattern.htm http://en.wikipedia.org/wiki/Strategy_pattern

1 comment: