What is Strategy Pattern in Java?

It defines a family of algorithms, encapsulates each one, and make them interchangeable. Strategy lets the algorithms vary independently from clients that use it.

A problem to understand the need to implement Strategy Pattern in Java

A Robot manufacturing company wants to show off their “Robot Simulator” App to their clients to understand about their Robots for implementation.

  • Client A wants to implement Robot (performs only watching/recording activity) replacing their CCTV cameras
  • Client B wants to purchase Robot (performs walking, running, talking with human) to accompany him as a friend
  • Client C wants to replace his human security with Robot Security (performs walking, running, flying)

Another client might require Robot to perform all the activity of recording, walking, running, talking, flying ( Like an Iron Man .. Yaahhhh…)

The requirement keeps changing for every client and our “Robot Simulator” App should be handled such a way to keep minimum code changes while implementing the ever changing requirement for each client.

When to use Strategy pattern in Java?

When the requirement have multiple behaviors and each client wants different combination of behaviors to be implemented

Let’s take a look at the code snippets to understand the implemented Strategy Pattern in Action. From the above requirement we have different behaviors to perform for Robots such as running, walking, flying, talking, recording – so we are going make those behaviors as Interfaces.

Note: Lets consider only running, walking, flying behaviors. Upon understanding you will be able to implement multiple behaviors as required.

public interface FlyBehavior {

  public void performFly();
}
public interface RunBehavior {

  public void performRun();
}
public interface WalkBehavior {

  public void performWalk();
}

Now we have Interfaces for each behaviors to implement for Robot. But every client doesn’t require all behaviors to be implemented but we want to code including all behaviors and restrict unwanted behaviors while implementing for every new requirement.

So we are going to have multiple behaviors under Walking, Running, Flying as below to dynamically choose required behaviors.

public class RunWithSpeed implements RunBehavior{

  @Override
  public void performRun() {
    System.out.println("I'm going to run in normal speed");
  }

}
public class RunNoWhere implements RunBehavior {

  @Override
  public void performRun() {
    System.out.println("I don't run");
  }

}
public class WalkWithSpeed implements WalkBehavior {

  @Override
  public void performWalk() {
    System.out.println("I'm going to walk in normal speed");		
  }

}
public class WalkNoWhere implements WalkBehavior {

  @Override
  public void performWalk() {
    System.out.println("I don't walk anywhere");
  }

}
public class FlyinFlightMode implements FlyBehavior {

  @Override
  public void performFly() {
    System.out.println("I'm in flight mode");		
  }

}
public class FlyNoWhere implements FlyBehavior {

  @Override
  public void performFly() {
    System.out.println("I don't fly anywhere");
  }

}

Now lets create an abstract Robot.java class to accommodate all behaviors and actions.

public abstract class Robot {

  public WalkBehavior walkBehavior;
  public RunBehavior runBehavior;
  public FlyBehavior flyBehavior;
  
  public abstract void performAction();
  
  public void performWalk() {
    walkBehavior.performWalk();
  }
  
  public void performRun() {
    runBehavior.performRun();
  }
  
  public void performFly() {
    flyBehavior.performFly();
  }
  
  public void startRobotActivity() {
    // All robots needs to be started
    System.out.println("Robot is starting ....");
  }
}

Now using Robot class we can create multiple Robots such as RobotCCTV, CompanionRobot, SecurityRobot as below,

RobotCCTV is initialized in a way that it won’t walk or run or fly anywhere

public class RobotCCTV extends Robot{

  // This robot is initialized as it won't walk or run anywhere
  public RobotCCTV() {
    walkBehavior = new WalkNoWhere();
    runBehavior = new RunNoWhere();
    flyBehavior =  new FlyNoWhere();
  }
  
  @Override
  public void performAction() {
    System.out.println("I'm going to record human activities...");
    
  }

}

CompanionRobot is initialized as it will walk/run but won’t fly no where.

public class CompanionRobot extends Robot{
  
  // This robot is initialized as it will walk/run with humans
  public CompanionRobot() {
    walkBehavior =  new WalkWithSpeed();
    runBehavior = new RunWithSpeed();
    flyBehavior =  new FlyNoWhere();
  }

  @Override
  public void performAction() {
    System.out.println("I'm going to accompany my human friend....");
    
  }

}

SecurityRobot is initialized as it will walk/run/fly

public class SecurityRobot extends Robot{
  // This robot is initialized as it will walk/run/fly 
    public SecurityRobot() {
      walkBehavior =  new WalkWithSpeed();
      runBehavior = new RunWithSpeed();
      flyBehavior =  new FlyinFlightMode();
    }

    @Override
    public void performAction() {
      System.out.println("I'm going to secure my human friend....");
      
    }

}

Now let’s create our simulator to simulate each type of Robot.

public class RobotSimulator {

  public static void main(String[] args) {
    System.out.println("---------------RobotCCTV----------------------");
    RobotCCTV cctv = new RobotCCTV();
    cctv.performFly();
    cctv.performRun();
    cctv.performWalk();
    
    System.out.println("---------------CompanionRobot-----------------");
    CompanionRobot companionRobot = new CompanionRobot();
    companionRobot.performWalk();
    companionRobot.performRun();
    companionRobot.performFly();
    
    System.out.println("---------------SecurityRobot------------------");
    SecurityRobot securityRobot = new SecurityRobot();
    securityRobot.performWalk();
    securityRobot.performRun();
    securityRobot.performFly();
    
  }
}

Output:

---------------RobotCCTV----------------------
I don't fly anywhere
I don't run
I don't walk anywhere
---------------CompanionRobot-----------------
I'm going to walk in normal speed
I'm going to run in normal speed
I don't fly anywhere
---------------SecurityRobot------------------
I'm going to walk in normal speed
I'm going to run in normal speed
I'm in flight mode

Right now, we have WalkBehavior, RunBehavior, FlyBehavior getting initialized in constructor but when we have options to go dynamically being static is a shame.

Setting behavior dynamically

We are going to revise the RobotCCTV, CompanionRobot, SecurityRobot class for setting behavior dynamically. Instead of initializing behaviors in constructor, we are going create setter method to set behaviors during runtime.

public class RobotCCTV extends Robot{

  public void setWalkBehavior(WalkBehavior walkBehavior) {
    this.walkBehavior = walkBehavior;
  }
  
  public void setRunBehavior(RunBehavior runBehavior) {
    this.runBehavior = runBehavior;
  }
  
  public void setFlyBehavior(FlyBehavior flyBehavior) {
    this.flyBehavior = flyBehavior;
  }
  
  @Override
  public void performAction() {
    System.out.println("I'm going to record human activities...");
    
  }

}
public class CompanionRobot extends Robot{
  
  public void setWalkBehavior(WalkBehavior walkBehavior) {
    this.walkBehavior = walkBehavior;
  }
  
  public void setRunBehavior(RunBehavior runBehavior) {
    this.runBehavior = runBehavior;
  }
  
  public void setFlyBehavior(FlyBehavior flyBehavior) {
    this.flyBehavior = flyBehavior;
  }

  @Override
  public void performAction() {
    System.out.println("I'm going to accompany my human friend....");
    
  }

}
public class SecurityRobot extends Robot {

  public void setWalkBehavior(WalkBehavior walkBehavior) {
    this.walkBehavior = walkBehavior;
  }

  public void setRunBehavior(RunBehavior runBehavior) {
    this.runBehavior = runBehavior;
  }

  public void setFlyBehavior(FlyBehavior flyBehavior) {
    this.flyBehavior = flyBehavior;
  }

  @Override
  public void performAction() {
    System.out.println("I'm going to secure my human friend....");

  }

}

Go for dynamic simulator for any client for any Robot

public class RobotSimulator {

  public static void main(String[] args) {
    System.out.println("---------------RobotCCTV----------------------");
    RobotCCTV cctv = new RobotCCTV();
    cctv.setFlyBehavior(new FlyNoWhere());
    cctv.setRunBehavior(new RunNoWhere());
    cctv.setWalkBehavior(new WalkNoWhere());
    cctv.performFly();
    cctv.performRun();
    cctv.performWalk();
    
    System.out.println("---------------CompanionRobot-----------------");
    CompanionRobot companionRobot = new CompanionRobot();
    companionRobot.setFlyBehavior(new FlyNoWhere());
    companionRobot.setRunBehavior(new RunWithSpeed());
    companionRobot.setWalkBehavior(new WalkWithSpeed());
    companionRobot.performWalk();
    companionRobot.performRun();
    companionRobot.performFly();
    
    System.out.println("---------------SecurityRobot------------------");
    SecurityRobot securityRobot = new SecurityRobot();
    securityRobot.setFlyBehavior(new FlyinFlightMode());
    securityRobot.setRunBehavior(new RunWithSpeed());
    securityRobot.setWalkBehavior(new WalkWithSpeed());
    securityRobot.performWalk();
    securityRobot.performRun();
    securityRobot.performFly();
    
  }
}

Output

I don't fly anywhere
I don't run
I don't walk anywhere
---------------CompanionRobot-----------------
I'm going to walk in normal speed
I'm going to run in normal speed
I don't fly anywhere
---------------SecurityRobot------------------
I'm going to walk in normal speed
I'm going to run in normal speed
I'm in flight mode

UML Diagram Overview

Conclusion

While implementing patterns, pay careful attention to the relationships between the classes. Write appropriate relationships (IS-A, HAS-A and IMPLEMENTS).

Strategy Patterns defines a family of algori

thms, encapsulates each one, and make them interchangeable. Strategy lets the algorithms vary independently from clients that use it.

Please follow and like us: