Real World Example of the Strategy Pattern

Viewed 96515

I've been reading about the OCP principle and how to use the strategy pattern to accomplish this.

I was going to try and explain this to a couple of people, but the only example I can think of is using different validation classes based on what status an "order" is.

I've read a couple of articles online, but these don't usually describe a real alike reason to use the strategy, like generating reports/bills/validation, etc...

Are there any real-world examples where you think a strategy pattern is common?

19 Answers

What about this:

You have to encrypt a file.

For small files, you can use "in memory" strategy, where the complete file is read and kept in memory ( let's say for files < 1 gb )

For large files, you can use another strategy, where parts of the file are read in memory and partial encrypted results are stored in tmp files.

These may be two different strategies for the same task.

The client code would look the same:

 File file = getFile();
 Cipher c = CipherFactory.getCipher( file.size() );
 c.performAction();



// implementations:
interface  Cipher  {
     public void performAction();
}

class InMemoryCipherStrategy implements Cipher { 
         public void performAction() {
             // load in byte[] ....
         }
}

class SwaptToDiskCipher implements Cipher { 
         public void performAction() {
             // swapt partial results to file.
         }

}

The

     Cipher c = CipherFactory.getCipher( file.size() );

Would return the correct strategy instance for the cipher.

I hope this helps.

( I don't even know if Cipher is the right word :P )

I can think of several fairly simple examples:

  • Sorting a list. The strategy is the comparison used to decide which of two items in the list is "First"
  • You might have an application where the sorting algorithm itself (QuickSort, HeapSort, etc.) may be chosen at runtime
  • Appenders, Layouts, and Filters in Log4Net and Log4j
  • Layout Managers in UI toolkits
  • Data compression. You might have an ICompressor interface whose sole method looks something like this:

    byte[] compress(byte[] input);

    Your concrete compression classes might be things like RunLengthCompression, DeflateCompression, etc.

One common usage of the strategy pattern is to define custom sorting strategies (in languages without higher-order functions), e.g. to sort a list of strings by length in Java, passing an anonymous inner class (an implementation of the strategy interface):

List<String> names = Arrays.asList("Anne", "Joe", "Harry");
Collections.sort(names, new Comparator<String>() {
  public int compare(String o1, String o2) {
    return o1.length() - o2.length();
  }
});
Assert.assertEquals(Arrays.asList("Joe", "Anne", "Harry"), names);

In a similar manner, strategies can be used for native queries with object databases, e.g. in db4o:

List<Document> set = db.query(new Predicate<Document>() {
  public boolean match(Document candidate) {
    return candidate.getSource().contains(source);
  }
});

I have an application that synchronizes it's user base each day against our enterprise directory. User's are eligible or not eligible based on their status in the University. Each day the provisioning program goes through and makes sure that those who are supposed to be eligible are provisioned in the application and those who are not are de-provisioned (actually according to a graceful degradation algorithm, but that's beside the point). On Saturday I do a more thorough update that synchronizes some properties of each user as well as making sure that they have the proper eligibility. At the end of the month I do some bill back processing based on usage for that month.

I use a composable strategy pattern to do this synchronization. The main program basically chooses a master strategy depending on the day of the week (sync changes only/sync all) and the time of semester relative to the academic calendar. If the billing cycle is ending, then it also composes it with a billing strategy. It then runs the chosen strategy via a standard interface.

I don't know how common this is, but I felt like it was a perfect fit for the strategy pattern.

Are you sure that the status of an "order" is not a State pattern? I have a hunch that an order will not be handled differently depending on its status.

Take for example the method Ship on the Order:

order.Ship();
  • If the shipping method varies in function of its status, then you've got a strategy pattern.
  • If however the Ship() method succeeds only when the order has been paid, and the order has not been shipped yet, you've got a state pattern.

The best example of the state pattern (and other patterns) I found was in the book "Head First Design Patterns", which is amazing. A close second will be David Cumps' blogging series of patterns.

I used the strategy approach in a fairly complex engine in an application that is a good example. Essentially the engine's role was to go and first find a list of people who had a widget, it's second role was to figure out which were the 10 best people with a widget based on an unknown number of parameters (things like price distance previous business together, ammount on stock, shipping options etc etc etc...)

Essentially what we did was we broke the problem into two strategies the first being data retrieval, as we knew that we had multiple sources of our widgets and we needed to be able to get the data and transform it to a common structure.

We then also realized that we had multiple algorithims some were based on weighting the parameters, others were very weird and propitery and I couldn't do them justice without pulling out visios and charts and well you get the picture, we had lots of algorithims for selecting the best people.

Our service itself was very thing it essentially defined the inputs, outputs and did some normalization of the data, it also used a provider pattern to plug-in the application specific data providers and algorithim providers which used the strategy. This was a fairly effective system.

We had some debates if we were using a strategy or a template pattern which we never resolved.

A few weeks ago, I added a common Java interface which was implemented by one of our domain objects. This domain object was loaded from the database, and the database representation was a star schema with about 10+ branches. One of the consequences of having such a heavy weight domain object is that we've had to make other domain objects that represented the same schema, albeit less heavyweight. So I made the other lightweight objects implement the same interface. Put otherwise we had:

public interface CollectibleElephant { 
    long getId();
    String getName();
    long getTagId();
}

public class Elephant implements CollectibleElephant { ... }
public class BabyElephant implements CollectibleElephant { ... }

Originally, I wanted to use CollectibleElephant to sort Elephants. Pretty quickly, my teammates glommed onto CollectibleElephant to run security checks, filter them as they get sent to the GUI, etc.

We had to create a third-party provisioning interface for an enterprise platform with a very complicated database. The submission of data to be provisioned was as a list of our data types which were put into a priority queue in our application so they could be written to the database in the correct order due to dependencies.

The process to write that data was then quite simple, keep popping off the top of the priority queue and then choose a strategy based on the type of the object that you extract.

Example to calculate Item GST Tax Total

public interface TaxCalculation {

    public Double calculateTax(Double price);  
}
public class FivePercentage implements TaxCalculation {

    @Override
    public Double calculateTax(Double price) {
        
        Double dbl = (price*5)/100;
        return dbl;
    }

}
public class EighteenPercentage implements TaxCalculation {

    @Override
    public Double calculateTax(Double price) {
        Double dbl = (price*18)/100;
        return dbl;
    }

}
public class Item {

    public String name;
    public Double price;
    public int taxRate;
    public Double totalTax;
    
    public Item(String name, Double price, int taxRate) {
        super();
        this.name = name;
        this.price = price;
        this.taxRate = taxRate;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getPrice() {
        return price;
    }
    public void setPrice(Double price) {
        this.price = price;
    }
    public int getTaxRate() {
        return taxRate;
    }

    public void setTaxRate(int taxRate) {
        this.taxRate = taxRate;
    }

    public Double getTotalTax() {
        return totalTax;
    }

    public void setTotalTax(Double totalTax) {
        this.totalTax = totalTax;
    }

    public void calculateTax(TaxCalculation taxcalulation, Double price) {
        this.totalTax = taxcalulation.calculateTax(price);
    }
    
    @Override
    public String toString() {
        return "Items [name=" + name + ", price=" + price + ", taxRate=" + taxRate + ", totalTax=" + totalTax + "]";
    }
    
}
public class CalculateTax {

    public static void main(String[] args) {
        
        List<Item> itemList = new ArrayList<>();
        
        Item item1 = new Item("Engine Oil", 320.0, 5);
        Item item2 = new Item("Painting", 3500.00, 18);
        
        itemList.add(item1);
        itemList.add(item2);
        
        itemList.stream().forEach(x-> {
            if(x.getTaxRate() == 5) {
                x.calculateTax(new FivePercentage(), x.getPrice());
            } else if(x.getTaxRate() == 18) {
                x.calculateTax(new EighteenPercentage(), x.getPrice());
            }
        });
        
        itemList.stream().forEach(x-> {
            System.out.println(x.toString());
        });
    }
}

From wikipedia

In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use

In Windows Paint application you can see a strategy pattern where you can choose the shape and color independently in a different sections. Here the shape and color are the algorithms which can changed at runtime.

If you want to draw a circle with red color, rather than providing an option of 'RedCircle' they let you choose the circle and a color of your choice.

Shape redCircle = new RedCircle(); // Without stretegy Pattern
Shaped redCircle = new Shape("red","circle"); // With Strategy pattern

Without strategy pattern will increase the number of classes with the Cartesian product of shape and color. Also the interface changes for each implementation.

Imagine a shooter game with AI enemies for example. You want them to continuously fight in different ways based on what happens .. With strategy pattern you can continuously loop and dynamically change how a specific or action will be performed.

interface FightingStategy{
    public void fight();
}
public Defense implements FightingStrategy{
    public void figth(){
        ... hide behind wall to shoot
    }
}
public Berserker implements FightingStrategy{
    public void fight(){
        ... run towards you, headrolls and shoots
    }
}
public Dead implements FightingStrategy{
    public void fight(){
        ... is dead, doesn't move
    }
}

public AiShooter{

    FightingStrategy fightingStrategy;

    public AiShooter(){
        fightStrategy = new Berserker();
    }

    public void fight(){
        this.fightingStrategy.fight();
    }

    public void changeStrategy(FightingStrategy f){
        this.fightingStrategy = f;
    }
}

public static void main(){

    ... create list of AiShooters...
    while (condition){
        list.forEach(shooter -> shooter.fight());
    }
    ... you shoot back
    list.ForEach(shooter -> shooter.changeStrategy(new 
Defense()));

    ... you kill one
    list.get(n).changeStrategy(new Dead());
}

This answer is for someone who is a beginner and want to understand this with the simplest example possible - (Reference : Headfirst Design Patterns)

#include<iostream>
using namespace std;

/*
    Where it is applicable?
    The selection of an algorithm is required from a family of algorithms.
    To avoid multiple conditional statements for selection of algorithms
    and to hide its algorithm data structures and complexity from client.
*/

class Fly {
public:
    virtual void fly() = 0;
};

//concrete Fly : rocketFly
class rocketFly : public Fly {
public:
    void fly() {
        cout <<"rocketFly::fly()" << endl;
    }
};

//concrete Fly : normalFly
class normalFly : public Fly {
public:
    void fly() {
        cout <<"normalFly::fly()" << endl;
    }
};

//Duck "HAS A" relationship with Fly
class Duck {
private:
    //Duck has a Fly behavour
    Fly* flyObj;
public:
    Duck(Fly* obj) : flyObj(obj) {
        
    }

    void DuckFly() {
        flyObj->fly();
    }
};

int main() {
    rocketFly* rObj = new rocketFly;
    Duck wildDuck(rObj);
    wildDuck.DuckFly();

    normalFly* nObj = new normalFly;
    Duck cityDuck(nObj);
    cityDuck.DuckFly();

    /*
        I didn't have to create classes like wildDuck which inherits from Duck and they implement their own
        fly, quack etc behaviour. There will be code duplication.
        So, instead of that, create an interface of fly, make concrete fly classes with different
        fly behaviour. Use objects to any of these concrete classes to inject to one generic duck
        class which will automatically call correct fly behaviour.
    */

return 0;
}
Related