Design Patterns: The Strategy Pattern in Java
by Riley MacDonald, August 6, 2017

The strategy pattern is used to encapsulate functionality and maximize maintainability while exercising polymorphism and inheritance.

Simple inheritance example:
Given an abstract superclass Feline; We have two concrete implementations HouseCat and WildCat. Each of the classes inherit the walk() functionality, and implement their own meow() functionality:

// Feline superclass
public abstract class Feline {
    void meow();
    void walk() {
        // do walk
    }
}
 
public class HouseCat implements Feline {
    @Override
    public void meow() {
        // do soft meow
    }
}
 
public class WildCat implements Feline {
    @Override
    public void meow() {
        // do meow with growl
    }
}

Problem: Add new functionality to Feline:
Let’s say we want to add some functionality to the Felines so they can get food. Thinking in an Object Oriented mindset the easiest solution would be to add a new method getFood() to the Feline superclass. This way all the subclasses of Feline will inherit the functionality.

public abstract class Feline {
    void meow();
    void walk() {
        // do walk
    }
 
    // new method to get food
    void getFood();
}

On the surface this appears to be a good solution to the problem. But what if some of the existing Feline subclasses aren’t supposed to getFood()? What if other subclasses are added that shouldn’t getFood() or walk()? Now we have duplicated code across classes and empty implementations. Suddenly using inheritance for reuse has made maintenance difficult!

Solution – Utilize the Strategy Pattern
Let’s start by creating some interfaces which cover the different behaviors Felines have.

Meow Behavior:

// interface
public interface MeowBehavior {
    void meow();
}
 
// concrete implementations
public class MeowSoftly implements MeowBehavior {
    @Override
    public void meow() {
        System.out.println("meow...");
    }
}
 
public class MeowWithGrowl implements MeowBehavior {
    @Override
    public void meow() {
        System.out.println("urrrrrrrMEOW");
    }
}

Eat Behavior:

// interface
public interface GetFoodBehavior {
    void getFood();
}
 
 
// concrete implementations
public class GetProvidedFood implements GetFoodBehavior {
    @Override
    public void getFood() {
        // eat food from dish
    }
}
 
public class HuntForFood implements GetFoodBehavior {
    @Override
    public void getFood() {
        // hunt for food in the wilderness
    }
}

Now that we have some behaviors defined the functionality is no longer encapsulated within the Feline object and can be reused by other types of objects. Let’s upgrade the Feline class to utilize the Strategy Pattern.

Let’s add two new instance variables to the Feline class and delegate the existing meow() and getFood() methods out to the new behavior interfaces:

public abstract class Feline {
    // add the new behaviors as instance variables
    MeowBehavior meowBehavior;
    GetFoodBehavior getFoodBehavior;
 
    // delegate the behavior out to the behavior interfaces
    void meow() {
        meowBehavior.meow();
    }
 
    void getFood() {
        getFoodBehavior.getFood();
    }
}

Now we have encapsulated our meow() and getFood() functionality outside of the Feline class! Let’s spin up a new HouseCat / WildCat and implement the desired behavior using polymorphism.

public class HouseCat extends Feline {
    public HouseCat() {
        meowBehavior = new MeowSoftly();
        getFoodBehavior = new GetProvidedFood();
    }
}
 
public class WildCat extends Feline {
    public void WildCat() {
        meowBehavior = new MeowWithGrowl();
        getFoodBehavior = new HuntForFood();
    }
}

Modify Feline Behaviors at Runtime
Another advantage to using the Strategy Pattern is exposing the ability to modify an objects behavior at runtime. This wasn’t possible using the simple inheritance example. We can achieve this by exposing a behavior setter in the Feline class:

public abstract class Feline {
    // new methods to dynamically alter behavior at runtime
    void setMeowBehavior(MeowBehavior meowBehavior);
    void setGetFoodBehavior(GetFoodBehavior getFoodBehavior);
}

Implementation Example:
Now we have everything we need to implement our different cats using the Strategy Pattern. New functionality can be added at anytime without modifying any of our Behavior classes or the Feline superclass.

// Cats
Feline houseCat = new HouseCat();
Feline wildCat = new WildCat();
 
houseCat.meow();
// observe output -> "meow"
 
wildCat.meow();
// observe output -> "urrrrrrrMEOW"
 
// modify the meow behavior at runtime
houseCat.setMeowBehavior(new MeowWithGrowl());
wildCat.setMeowBehavior(new MeowSoftly());
 
houseCat.meow();
// observe output -> "urrrrrrrMEOW"
 
wildCat.meow();
// observe output -> "meow"

Outstanding Question:
While I feel I have a good understanding of the Strategy Design Pattern one thing sticks out. What if a new subclass of Feline is written but the developer doesn’t assign behavior implementations to the instance variables? What’s the best solution for this?

  • Create a default behavior and assign it in the abstract superclass?
  • Add a construct which forces developers to pass in behaviors at initialization?
  • Throw an exception if no assignment is made?
Open the comment form

Leave a comment:

Comments will be reviewed before they are posted.

User Comments:

Design Patterns: The State Pattern in Java | Riley MacDonald on 2017-10-29 23:46:18 said:
[…] state pattern is very similar to the Strategy Pattern as it allows the behavior of objects to be altered at runtime. Objects behavior will be based on […]

Design Patterns: The Template Pattern in Java | Riley MacDonald on 2017-10-09 00:00:26 said:
[…] “Don’t call us, we’ll call you.”). This pattern is similar to the Strategy Pattern as it encapsulates algorithms and delegates responsibilities to implementations / […]