Thursday, 5 December 2013

Strategy pattern / Collections.sort()

algorithm's behaviour can be selected at runtime.  

the strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. 

Strategy pattern is one of the behavioral design pattern. Strategy pattern is used when we have multiple algorithm for a specific task and client decides the actual implementation to be used at runtime.
Strategy pattern is also known as Policy Pattern. We defines multiple algorithms and let client application pass the algorithm to be used as a parameter. One of the best example of this pattern is Collections.sort() method that takes Comparator parameter. Based on the different implementations of Comparator interfaces, the Objects are getting sorted in different ways



public class Student {

      private int rollNo;
      private String fname;
      private String lname;

package strategyP;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class TestStudent {

      public static void main(String[] args) {

            ArrayList<Student> studentList = new ArrayList<Student>();

            studentList.add(new Student(1, "Aditya", "Sharma"));
            studentList.add(new Student(2, "Beena", "Mishra"));
            studentList.add(new Student(3, "Priyanka", "Pandey"));
            studentList.add(new Student(4, "Vikas", "Jain"));
            studentList.add(new Student(5, "Rose", "Marlo"));

            Comparator<Student> comparasionStrategy = new Comparator<Student>() {

                  @Override
                  public int compare(Student arg0, Student arg1) {

                        return arg0.getFname().compareTo(arg1.getFname());

                  }
            };
     
            Collections.sort(studentList,comparasionStrategy);
           
            for (Student student : studentList) {
                  System.err.println(student.toString());
            }
           
            Collections.sort(studentList, new Comparator<Student>() {

                  @Override
                  public int compare(Student arg0, Student arg1) {
                        return arg1.getRollNo() - arg0.getRollNo();
                  }

            });

            for (Student student : studentList) {
                  System.out.println(student.toString());
            }

      }
}




Where Would I Use This Pattern(A More Realistic Example)

The Strategy pattern is to be used where you want to choose the algorithm to use at runtime. A good use of the Strategy pattern would be saving files in different formats, running various sorting algorithms, or file compression.
The Strategy pattern provides a way to define a family of algorithms, encapsulate each one as an object, and make them interchangeable.  



package strategyP;

import java.io.File;
import java.util.ArrayList;

Let's use the example of a file compression tool - where we create either zip or rar files. First we'll need a strategy: 


interface CompressionStrategy {
      public void compressFiles(ArrayList<File> files);
}


And we'll need to provide our two implementations, one for zip and one for rar

class ZipCompressionStrategy implements CompressionStrategy {

      public void compressFiles(ArrayList<File> files) {
            // using ZIP approach
      }

}

class RarCompressionStrategy implements CompressionStrategy {

      public void compressFiles(ArrayList<File> files) {
            // using RAR approach
      }
}

Our context will provide a way for the client to compress the files. Let's say that there is a preferences setting in our application that sets which compression algorithm to use. We can change our strategy using the setCompressionStrategy method in the Context.

class CompressionContext {
      private CompressionStrategy strategy;

      // this can be set at runtime by the application preferences
      public void setCompressionStrategy(CompressionStrategy strategy) {
            this.strategy = strategy;
      }

      // use the strategy
      public void createArchive(ArrayList<File> files) {
            strategy.compressFiles(files);
      }

}

It's obvious that all the client has to do now is pass through the files to the CompressionContext

public class Client {

      public static void main(String[] args) {
            CompressionContext ctx = new CompressionContext();
            // we could assume context is already set by preferences
            ctx.setCompressionStrategy(new ZipCompressionStrategy());
            // get a list of files

            ArrayList<File> fileList = null;
            ctx.createArchive(fileList);

      }
}

This example shows the WarStrategy




package strategyP;

interface WarStrategy {

      void strategyDisc();

}

class Surprise implements WarStrategy {

      @Override
      public void strategyDisc() {
            System.out.println("Surprise your enemies");
      }

}

class Maneuver implements WarStrategy {

      @Override
      public void strategyDisc() {
            System.out
                        .println("Place the enemy in a disadvantageous position through the flexible application of combat power");
}

}

class Guerrilla implements WarStrategy {

      @Override
      public void strategyDisc() {
            System.out
                        .println("Lure your opponent to attack to use guerrilla tactics effectively.");
      }
}



public class War {

      WarStrategy strategy;

      public WarStrategy getStrategy() {
            return strategy;
      }

      public void setStrategy(WarStrategy strategy) {
            this.strategy = strategy;
      }

      public static void main(String[] args) {

            War worldWarI = new War();
            worldWarI.setStrategy(new Surprise());
            worldWarI.getStrategy().strategyDisc();

            War worldWarII = new War();
            worldWarII.setStrategy(new Guerrilla());
            worldWarII.getStrategy().strategyDisc();

            War worldWarIII = new War();
            worldWarIII.setStrategy(new Maneuver());
            worldWarIII.getStrategy().strategyDisc();

      }

}











2 comments:

  1. public class StratigyP {
    int operate(int x, int y, Operation strategy) {
    return (Integer) strategy.performOperation(x, y);
    }
    public static void main(String[] args) {
    StratigyP p = new StratigyP();
    System.out.println(p.operate(6, 2, new Multiply()));
    System.out.println(p.operate(6, 2, new Sum()));
    }
    }

    interface Operation {
    T performOperation(T x, T y);
    }
    class Sum implements Operation {
    @Override
    public Integer performOperation(Integer x, Integer y) {
    return y + x;
    }
    }
    class Multiply implements Operation {
    @Override
    public Integer performOperation(Integer x, Integer y) {
    return y * x;
    }
    }

    ReplyDelete
  2. Nice explanation with adequate example!thanks for this.

    ReplyDelete