Getting down with the Strategy pattern

It’s time to talk patterns. They’ll help you, they’ll help me, what’s not to like? The first one we’ll talk about is the Strategy pattern. Formally, the Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
In other words, when you have a set of objects that perform a similar–yet different–set of algorithms, chances are that abstracting those algorithms away from the clients that use them is a good bet.

Let’s say you’ve got a GuitarSimulator. This app simulates sounds of two types of guitars: an acoustic guitar and a single-coil, solid-body electric guitar–clean setting. The app is used by guitar instructors to teach guitar.

Using classic OO techniques, you have an abstract superclass guitar that defines a method, performSound(), that is implemented by Guitar because all guitars make sound.

public abstract class Guitar {
    public void performSound() {
        System.out.println("strrruuummmm");
    }
}

Our acoustic guitar class looks like this:

public class Acoustic extends Guitar {
}

…and finally, the electric guitar class:

public class Electric extends Guitar {
}

But then, somebody decides it would be a good idea to teach Guitar Hero skills since it’s so popular. So, somebody adds a Guitar Hero guitar to the library:

public class GuitarHero extends Guitar {
}

That’s okay, right? But wait…you call the performSound() method, and what happens? “strrruuummmm”. Not good. Guitar Hero guitars don’t make a strum sound. They make a plastic clicky sound when you press the buttons.

One solution is to override performSound() in GuitarHero, to basically make it produce nothing. But what if later we come along and want to use the app to teach Air guitar? We basically would have to override performSound() in every subclass that doesn’t sound like “strrruuummmm”. And, what if they want the guitars in the app to do other things, like tune()? This solution quickly becomes unwieldy.

The problem with using inheritance, as opposed to encapsulation, to produce guitar behavior is fourfold:
– Code is duplicated across subclasses.
– Runtime behavior changes are difficult.
– It’s hard to gain knowledge of all guitar behaviors.
– Changes can unintentionally affect other guitars.

The strategy pattern comes to the rescue! With the strategy pattern, you define a set of algorithms and encapsulate them (i.e. “make them objects”).

With our GuitarSimulator, it looks like this. First, we introduce the algorithm encapsulated. To do this, we first create an interface that defines a makeSound() method to be implemented by all concrete objects of the algorithm:

public interface SoundBehavior {
    public void makeSound();
}

Then, we implement the interface in our different sounds:

public class UnamplifiedGuitarSound implements SoundBehavior {
    public void makeSound() {
        System.out.println("A nice, bright unamplified strummmm");
    }
}
public class AmplifiedGuitarSound implements SoundBehavior {
    public void makeSound() {
        System.out.println("Amplified strummmm");
    }
}
public class PlasticSound implements SoundBehavior {
    public void makeSound() {
        System.out.println("Clicky clicky");
    }
}

Now, let’s change our Guitar class. Instead of defining (and implementing) performSound(), we will create a member of type SoundBehavior. Then, in performSound() we will simply turn around and call our SoundBehavior’s makeSound() method. Sound complicated? I’ll show you:

public abstract class Guitar {
    SoundBehavior soundBehavior;

    public void performSound() {
        soundBehavior.makeSound();
    }
}

Finally, let’s create our concrete Guitars. We’ll assign the necessary behaviors in the individual constructors:

public class AcousticGuitar extends Guitar {

    public AcousticGuitar() {
        soundBehavior = new UnamplifiedGuitarSound();
    }
}
public class ElectricGuitar extends Guitar {

    public ElectricGuitar() {
        soundBehavior = new AmplifiedGuitarSound();
    }
}
public class RockBandGuitar extends Guitar {

    public RockBandGuitar() {
        soundBehavior = new PlasticSound();
    }
}

Notice that we have assigned concrete implementations of algorithms to the SoundBehavior member in each Guitar object. With that, we can create our executable simulator code:

public class GuitarSim {
    public static void main(String[] args) {
        Guitar acousticGuitar = new AcousticGuitar();
        Guitar electricGuitar = new ElectricGuitar();
        Guitar rockBandGuitar = new RockBandGuitar();

        acousticGuitar.performSound();
        electricGuitar.performSound();
        rockBandGuitar.performSound();
    }
}

For our output, we get:

A nice, bright unamplified strummmm
Amplified strummmm
Clicky clicky

Notice that we have no knowledge of the innards–we just call the guitar’s performSound(). Also, and more importantly, if we want to add a new AirGuitar object, we don’t need to muck around in the existing code. We create the behavior implementation (if we need a new behavior), then the AirGuitar object, and in it assign the correct behavior to the member SoundBehavior object. Awesome, huh?

Advertisements

About buffalobillion

Web Developer, JavaScript Balrog, Java dude, Ruby/Rails enthusiast. Guitar Playa.
This entry was posted in Programming. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s