Strategy Pattern Examples

What is the strategy pattern?

The Strategy pattern is a software design pattern that lets a class change its behavior at run time; helping to keep classes decoupled and re-usable.

Run time? Behavior?

I really like this description from Stackoverflow which seems like a real world use

(those duck and animal examples just don’t cut it for me!):

“A game where we can have different characters and each character can have multiple weapons to attack but at a time can use only one weapon”

Stackoverflow

In this case, we could have a character class and also some weapon classes like gun or sword. The weapon classes may subscribed to an interface that says each weapon needs to implement an Attack() method. That way, the character class only needs to know about the interface and could accept any type of weapon at a given time.

Let’s take another example…

A calculator that wants to log it’s activity

Suppose you have a calculator class and all it does is crunch numbers. Add, subtract, etc.

You then want to extend that class to log usage to a file or a database.

Should you add that new behavior to your calculator class? You could but that would start to bloat your calculator class.

What about letting sub-classes (that inherit the Calculator class) implement logging behavior? Again, you could do this but the problem is what happens when two different sub classes want to log their activity. Now you end up with the same code possibly in two locations. Not good!

Instead let’s use the Strategy pattern! We will create an interface just for the logging and tell our Calculator class it should know about that interface. This allows our Calculator class to leverage any logging class that wants to implement that interface. And our Calculator class doesn’t need to know details about logging, just that it can log.

Here is some code (PHP) to illustrate:

<?php

interface Loger {
    function log();
}

class FileLoger implements Loger {
    function log() {
        print "now logging info to a file...\n";
    }
}

class DatabaseLoger implements Loger {
    function log() {
        print "now logging info to a database...\n";
    }
}

class Calculator {
    private $log;

    function __construct(Loger $log) {
        $this->log = $log;
    }

    function log() {
        $this->log->log();
    }
}

class ScientificCalculator extends Calculator {
    function someAdvancedFeature() {
        // tbd...
    }
}

$fileloger = new FileLoger();
$calculator = new Calculator($fileloger);
$calculator->log();

$fileloger2 = new DatabaseLoger();
$calculator2 = new Calculator($fileloger2);
$calculator2->log();

And here is the output

now logging info to a file...
now logging info to a database...

In the above code we have a Calculator class that has a private member variable $log.

We also have a Loger interface with concrete implementations FileLoger and DatabaseLoger to log to files and to a database, respectively.

When we create a new Calculator object, we also first created a FileLoger object and passed that into the constructor of the Calculator object. And our Calculator constructor is expecting a variable of interface type Loger; it doesn’t care about any concrete implementations of Loger. The Calculator class could accept a FileLoger or DatabaseLoger object and it doesn’t mater. It only knows about the Loger interface.

So the constructor stores the local $log variable to it’s private $log variable.

Then later we call the Calculator’s log() function, which in turn calls the private $log variable’s log() function, which again is defined in the Loger interface and that’s all the Calculator class knows about. It doesn’t care about how anyone implemented the log() function.

And we can create another Calculator after that with a different concrete implementation of the Loger interface and log to a Database. Or someone could create another concrete implementation to log to an API.

What’s also nice is we’ve got a ScientificCalculator class that inherits from the Calculator class and now it too can leverage objects that implement Loger and begin logging usage. It’s not shown in the code, but if you pass a Loger object into the ScientificCalculator constructor you’d get the same behavior as the Calculator object.

Decrypting a file based on file size

I like this example from a Stackoverflow contributor who actually used the Factory pattern along with the Strategy pattern to determine how to do data encryption (the algorithm that can change at run time) based on the size of the file in question.

I’ve copied and re-written the example here (and the language is Java):

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

interface Cipher  {
    public void performAction();
}

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

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

In the above example

  • For small files less than 1 gigabyte, the InMemory class should be used to performAction(); meaning the entire file is read and kept in memory and the decryption is done there.
  • Larger files should use the SwapToDisk class to performAction(); meaning parts of the file are read to memory and partial encrypted results are stored in tmp files.
  • Line 2 is the Factory which will get the correct class object based on the file size. The Factory implementation is not shown and this line is just to give you an idea the Factory does exist.
  • And line 3 is where the rubber meets the road and the Strategy pattern is used to run the performAction method (from the appropriate object).

So, this example differs slightly from the Calculator. With the Calculator we injected the Logger type into the Calculator object manually at run time. With the decryption example things are more dynamic and it’s the file size that determines which implementation (which object) of performAction() to use.

And now we can see the power of the strategy pattern. Super useful. It helps to keep our classes decoupled and re-usable.

Very cool! 🙂