Codiwan.com

The blog for Design Patterns, Linux, HA and Myself!

Strategy Pattern With Real World Example In Java

Strategy Pattern or Strategy Design Pattern: Learn Strategy Design Pattern by understanding a Real World example of hierarchies that involves interfaces, inheritance and ducks!

In this tutorial we’ll be learning about the Strategy Design Pattern. We’ll be creating some classes for Ducks using the Inheritance to pass on the behaviour from parents to the children. While doing so, we’ll be encountering some design challenges, and then we’ll be using the Strategy Design Pattern to fix those issues. In the second part, we’ll be looking into some helpful real world examples created using Java APIs, and then we’ll look into the use case scenarios in which the Strategy Design Pattern can be applied.

Flying Mallard Duck

We’ll start by creating a set of classes for

We’ll consider that the technique all the ducks use to swim and quack is same and as they are same, the methods swim() and quack will be present in the abstract duck class, however, the method , display(), characterizing the display properties, i.e., will be overridden by all concrete ducks, Red Head or the Mallard Duck.

Note: Make sure to go through the code comments as well. It’ll help you understand the concept better.

abstract class Duck {
    
    public String quack() {
        return "Quack!";
    }

    public String swim() {
        return "Swim!";
    }

    abstract public String display();

}

The Mallard Duck’s code with just the implementation of the display() method:

class MallardDuck extends Duck {

    public String display() {
        return "Looks like a Mallard Duck!";
    }
}

The Red Head Duck’s code, like Mallard Duck’s code, with just the implementation of the display() method:

class RedHeadDuck extends Duck {

    public String display() {
        return "Red head Duck has a red head!";
    }
}

So, what have we achieved here?

We’ve used the power of Inheritance to pass the swim() and quack() methods from the abstract duck to all the concrete ducks. This way all the new ducks will not have to care about the how to’s of these tasks as these will already be ingrained.

Well, our code looks good until now because we’ll be adding a new feature, FLY, for our ducks.

From the first glance, there seems to be an obvious solution to it then. Let’s make a fly method inside the abstract duck class and all the concrete ducks will be able to fly.

What’s the another solution? Maybe, we can keep the fly() method, in the abstract Duck class, abstract and allow all the concrete ducks to override it by providing their own method to fly.

Confused Duck

So, both of the solutions have failed. Let’s think through it.

All the flying behaviours that the ducks use are some standard algorithms. Most of them are not unique to a duck and they are common among them.

So, we find out that the fly behaviour is one which is varying on duck by duck basis. Let’s put this behaviour out then.

We’ll create a set of flying algorithms and the ducks will then use it. This way we’ll separate out the attributes that stays the same from the ones that vary a lot.

The varying attributes will be converted into a set of algorithms and it will be composed into the ducks.

Let’s start coding! We’ll start by creating multiple flying algorithms.

  1. FlyWithWings
  2. FlyNoWay

These algorithms or flying techniques will implement a Flybehavior interface. The FlyBehaviour is the extracted out part of the Duck that kept varying.

 // This is the extracted out behaviour.
 // Multiple concrete behaviours will be used by the Duck class to 
 // execute it's performFly method.
 // This behaviour is a must have for a duck.
 interface FlyBehaviour {
    String fly();
 }

The FlyWithWings behaviour:

class FlyWithWings implements FlyBehaviour {

    public String fly() {
        return "fly with two wings";
    }
}

The FlyNoWay behaviour(for the ducks that do not fly):

class FlyNoWay implements FlyBehaviour {

    @Override
    public String fly() {
        return "cannot fly";
    }
}

The changes that we’ve to make for accommodating the FlyBehaviour inside the Duck class:

abstract class Duck {

    private FlyBehaviour flyBehaviour;

    Duck(FlyBehaviour flyBehaviour) {
        if (flyBehaviour != null) {
            this.flyBehaviour = flyBehaviour;
        } else {
            throw new UnsupportedOperationException("bad fly behaviour");
        }
    }

    public String quack() {
        return "Quack!";
    }

    public String swim() {
        return "Swim!";
    }

    String performFly() {
        return flyBehaviour.fly();
    }

    abstract public String display();
}

If you notice the performFly() method you’ll find that the Duck doesn’t care about the type of the FlyBehaviour. It just calls the method fly. This is how we’ve removed the varying part from the duck class. Also, since this behaviour is being passed in the constructor it had become dynamic. This way the ducks can change their flying algorithms dynamically.

class MallardDuck extends Duck {

    MallardDuck(FlyBehaviour flyBehaviour) {
        super(flyBehaviour);
    }

    @Override
    public String display() {
        return "Looks like a Mallard Duck!";
    }
}

And, this is strategy design pattern.

The definition from the 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.

Real World Examples

As, we’ve a clear picture of the strategy design pattern, let’s look into two examples in which we’ll work with the Java APIs to create real world use cases to better understand this pattern.

In the first example I’ll be using the Comparator and List interfaces. I’ll create a list of Teams and then I’ll create two sorting strategies to sort the teams. The class for the Team is:

class Team {
    // stores the number of players
    int players;
    // stores the number of goals scored by the team
    int goalScored;
    // stores the number of goals scored against the team
    int goalAgainst;
}

It has three fields for its players count, goals that it has scored, and the goals that are scored against it. To understand the performance of the teams we would like to sort them on those fields. The Team class can implement the Comparable interface, and the sorting logic can reside inside the compareTo method. But there is a problem in that approach. Different fields will have different sorting logic and based on the season and other factors this logic will always keep on changing. So, instead of that approach I’ll create multiple comparators based on the requirements.

Here are the two comparators:

class PlayerCountComparator implements Comparator<Team> {
    public int compare(Team t1, Team t2) {
        return Integer.compare(t1.players, t2.players);
    }
}

class GoalDifferenceComparator implements Comparator<Team> {
    public int compare(Team t1, Team t2) {
        return Integer.compare(t1.goalScored - t1.goalAgainst, t2.goalScored - t2.goalAgainst);
    }
}

PlayerCountComparator sorts on the count of the players whereas GoalDifferenceComparator sorts on the difference between the goals scored and goals against.

Let’s create a list of the teams:

Random generator = new Random();
List<Team> teamList = new ArrayList<>(10);

for (int i = 0; i < 10; i++) {
    Team team = new Team(generator.nextInt() % 10, generator.nextInt() % 10, generator.nextInt() % 10);
    teamList.add(team);
}

Then to sort them we can use the List#sort method, for example:

teamList.sort(new PlayerCountComparator());

or

teamList.sort(new GoalDifferenceComparator());

Here I can pass any Comparator to sort the teamList and due to it the sorting logic is totally decoupled from the Team class or the teamList variable. This is same approach that we took for FlyBehaviour as well.

Let’s look into one more example to understand it better. Here’s a class for a Server.

class Server {

    public static void writeHostname(Writer w) throws IOException {
        String hostname = InetAddress.getLocalHost().getHostName();
        w.write(hostname);
        w.flush();
    }
}

The method writeHostname uses the writer(w) to write the hostname of the server. For the Server class it does not matter where the hostname will be written. All it cares is that it receives a Writer object to perform the write operation. This way the destination of the write() is completely out of picture for the Server. We can ask it to write on a File, like:

public static Writer getFileWriter() throws IOException {
    File file = new File("all_hostname.txt");
    boolean created = file.createNewFile();
    if (created) {
        return new FileWriter(file);
    }
    throw new IOException("cannot create a new file");
}

public static void main(String[] args) throws IOException {
    Writer fileWriter = getFileWriter();
    Server.writeHostname(fileWriter);
}

Or on the Standard Output:

public static Writer getStandardOutput() {
    return new OutputStreamWriter(System.out);
}

public static void main(String[] args) throws IOException {
    Writer soWriter = getStandardOutput();
    Server.writeHostname(soWriter);
}

It remains the same for the Server as the writing strategy is moved outside of the class.

Use Case Scenarios

  1. When there are a number of algorithms that can perform a job through different behaviours, like the FileWriter or the StandardOutput writer. Then Strategy Design Pattern provides a way to apply these algorithms.
  2. When the actual algorithm needs to be abstracted out so that client does not know about it. For example, in the Server class the writeHostname method is receiving a Writer instead of the OutputStreamWriter or the FileWriter.

I’ve created these tutorials after learning Design Patterns from this book Head First Design Patterns (A Brain Friendly Guide).

Please do add helpful comments about the content so that it can be made better.

Loading Comments... Disqus Loader
comments powered by Disqus