Strategy pattern

  1. Lecture
  2. Strategy pattern
    1. Usage
    2. Description
    3. Structure
  3. Example of implementation
    1. Application
    2. First step: Encapsulate the method
    3. Second step: Make your classes inherited from the interface/abstract class

  • Lecture

  • Strategy pattern

    • Usage

You should/could use this pattern when:

    • you need different versions of an algorithm that you can change at runtime; you will probably have to encapsulate the algorithm in a class
    • several related classes differ only in the way they behave; in this case you might have only to let them implement a parent interface or abstract class
    • Structure

The general structure of the Strategy Pattern is:

strategy

    • Description

The strategy pattern allows the behaviour of an algorithm to be changed during runtime. It defines a “family” of algorithms that are encapsulated to enable them to be swapped to carry out a specific behaviour.

  • Implementation


Let’s say you have three classes and one application:

Game.java

public class Game {

    private Hero hero;
    private Monster monster;

    // Constructor for a game, creating a hero and a monster
    public Game() {
       this.hero = new Hero("Hero", 10, 3);
       this.monster = new Monster("Monster", 10, 2);
    }
    // Getters
    public Hero getHero() { return this.hero; }
    public Monster getMonster() { return this.monster; }
    // Setters
    public void setHero(Hero hero) { this.hero = hero; }
    public void setMonster(Monster monster) { this.monster = monster; }
    // Makes a hero fight a monster
    public void fight(Hero hero, Monster monster){
       String heroName = hero.getName();
       String monsterName = monster.getName();

       System.out.println(heroName + " fights " + monsterName + "!");

       int charpunch = hero.getStrength();
       int monsterpunch = monster.getStrength();

       hero.loseHp(monsterpunch);
       monster.loseHp(charpunch);

       System.out.println(heroName + " has " + hero.getHp() + " hp left.");
       System.out.println(monsterName + " has " + monster.getHp() + " hp left.");

       if (monster.getHp() <= 0) {
          System.out.println(monsterName + " is dead.");
       }
       if (hero.getHp()<= 0){ 
          System.out.println(heroName + " is dead."); 
       } 
    } 
    // Calls the fight method until either the hero or the monster is dead
    public void play() { 
       System.out.println(this.hero.getName() + " is doing heroic things."); 
       System.out.println(this.hero.getName() + " sees " + this.monster.getName()); 
       while (this.hero.getHp()>0 && this.monster.getHp()>0){
          fight(this.hero, this.monster);
       }

       if (this.hero.getHp()>0){
          System.out.println(this.hero.getName() + " is doing heroic things again.");
       }
       else{
          System.out.println("GAME OVER");
       }
    }
}

Hero.java

public class Hero {
     private String name;
     private int hp;
     private int strength;
    // Constructor for a hero using his name, his hitpoints and strength
    public Hero(String name, int hp, int strength){
        this.name = name;
        this.hp = hp;
        this.strength = strength;
    }
    // Getters
    public String getName() { return this.name; }
    public int getHp() { return this.hp; }
    public int getStrength() { return this.strength; }
    // Setters
    public void setName(String name) { this.name = name; }
    public void setHp(int hp) { this.hp = hp; }
    public void loseHp(int hit){
        this.hp = (this.hp - hit < 0) ? 0 : (this.hp - hit) ;
    }
    public void setStrength(int strength) { this.strength = strength; }
}

Monster.java

public class Monster {
     private String name;
     private int hp;
     private int strength;
    // Constructor for a monster using his name, his hitpoints and strength
    public Monster(String name, int hp, int strength){
        this.name = name;
        this.hp = hp;
        this.strength = strength;

    }
    // Getters
    public String getName() { return this.name; }
    public int getHp() { return this.hp; }
    public int getStrength() { return this.strength; }
    // Setters
    public void setName(String name) { this.name = name; }
    public void setHp(int hp) { this.hp = hp; }
    public void loseHp(int hit){
        this.hp = (this.hp - hit < 0) ? 0 : (this.hp - hit) ;
    }
    public void setStrength(int strength) { this.strength = strength; }
}

Application.java

public class Application {

    public static void main(String[] args) {
        // Creates a game and calls the method play on it
        Game game = new Game();
        game.play();
    }
}

Output


Hero is doing heroic things.
Hero sees Monster
Hero fights Monster!
Hero has 8 hp left.
Monster has 7 hp left.
Hero fights Monster!
Hero has 6 hp left.
Monster has 4 hp left.
Hero fights Monster!
Hero has 4 hp left.
Monster has 1 hp left.
Hero fights Monster!
Hero has 2 hp left.
Monster has 0 hp left.
Monster is dead.
Hero is doing heroic things again.


We want to create different combat mode. One that will have harder fights, and the other will be the normal fight just as it is now. And we also want to be able to chose between those modes during the execution of the program. We will of course use the Strategy Pattern to accomplish that.

    • First Step: Encapsulate the method

The first step is to encapsulate the fight method in a class we will call FightStrategy. To do this, we just have to cut/paste the method from game to the new class.

To be able to use the fight() method from the Game, we add a FightStrategy attribute to the class, a setter for this attribute and we will call the fight method also from this attribute.

Game.java

public class Game {

    private Hero hero;
    private Monster monster;
    private FightStrategy fs;

    // Constructor for a game, creating a hero and a monster
    public Game() {
       this.hero = new Hero("Hero", 10, 3);
       this.monster = new Monster("Monster", 10, 2);
    }
    // Getters
    public Hero getHero() { return this.hero; }
    public Monster getMonster() { return this.monster; }
    // Setters
    public void setHero(Hero hero) { this.hero = hero; }
    public void setMonster(Monster monster) { this.monster = monster; }

    public void setFightStrategy(FightStrategy fs){
        this.fs = fs;
    }
    // Calls the fight method until either the hero or the monster is dead
    public void play() { 
       System.out.println(this.hero.getName() + " is doing heroic things."); 
       System.out.println(this.hero.getName() + " sees " + this.monster.getName()); 
       while (this.hero.getHp()>0 && this.monster.getHp()>0){
          this.fs.fight(this.hero, this.monster);
       }

       if (this.hero.getHp()>0){
          System.out.println(this.hero.getName() + "is doing heroic things again.");
       }
       else{
          System.out.println("GAME OVER");
       }
    }
}

FightStrategy.java

public class FightStrategy {

    public void fight(Hero hero, Monster monster){
       String heroName = hero.getName();
       String monsterName = monster.getName();

       System.out.println(heroName + " fights " + monsterName + "!");

       int charpunch = hero.getStrength();
       int monsterpunch = monster.getStrength();

       hero.loseHp(monsterpunch);
       monster.loseHp(charpunch);

       System.out.println(heroName + " has " + hero.getHp() + " hp left.");
       System.out.println(monsterName + " has " + monster.getHp() + " hp left.");

       if (monster.getHp() <= 0) {
          System.out.println(monsterName + " is dead.");
       }
       if (hero.getHp()<= 0){ 
          System.out.println(heroName + " is dead."); 
       } 
    } 
}

    • Second step: Make your classes inherited from the interface/abstract class

Now the program works exactly the same, but we want different behaviours for the fight method. So we will make the FightStrategy class an interface (in other cases an abstract class would work too) and then add to new classes that will inherit from FightStrategy. Of course, because this is just a quick example, their behaviours won’t differ much from one another. Here, we will just let the hero lose more hitpoints from one hit.

When you actually implements this pattern, make sure you have a better reason to use it than add 2 damages to a function because it then only duplicates code.

FightStrategy.java

public interface FightStrategy {

    public void fight(Hero hero, Monster monster);
}

FightNormal.java

public class FightNormal implements FightStrategy {

    public void fight(Hero hero, Monster monster){
       String heroName = hero.getName();
       String monsterName = monster.getName();

       System.out.println(heroName + " fights " + monsterName + "!");

       int charpunch = hero.getStrength();
       int monsterpunch = monster.getStrength();

       hero.loseHp(monsterpunch);
       monster.loseHp(charpunch);

       System.out.println(heroName + " has " + hero.getHp() + " hp left.");
       System.out.println(monsterName + " has " + monster.getHp() + " hp left.");

       if (monster.getHp() <= 0) {
          System.out.println(monsterName + " is dead.");
       }
       if (hero.getHp()<= 0){ 
          System.out.println(heroName + " is dead."); 
       } 
    } 
}

FightHard.java

public class FightHard implements FightStrategy {

    public void fight(Hero hero, Monster monster){
       String heroName = hero.getName();
       String monsterName = monster.getName();

       System.out.println(heroName + " fights " + monsterName + "!");

       int charpunch = hero.getStrength();
       int monsterpunch = monster.getStrength();
       // Hero loses 2 more hitpoints with each hits
       hero.loseHp(monsterpunch+2);
       monster.loseHp(charpunch);

       System.out.println(heroName + " has " + hero.getHp() + " hp left.");
       System.out.println(monsterName + " has " + monster.getHp() + " hp left.");

       if (monster.getHp() <= 0) {
          System.out.println(monsterName + " is dead.");
       }
       if (hero.getHp()<= 0){ 
          System.out.println(heroName + " is dead."); 
       } 
    } 
}

Application.java

public class Application {

    public static void main(String[] args) {
        // Creates a game and calls the method play on it
        Game game = new Game();
        game.setFightStrategy(new FightHard());
        game.play();
    }
}

Output


Hero is doing heroic things.
Hero sees Monster
Hero fights Monster!
Hero has 6 hp left.
Monster has 7 hp left.
Hero fights Monster!
Hero has 2 hp left.
Monster has 4 hp left.
Hero fights Monster!
Hero has 0 hp left.
Monster has 1 hp left.
Hero is dead.
GAME OVER