Skip to main content

Strategy Design Pattern

Problem​

You: Hi Shubh, today, I'm building a game where we get to be the king of our kingdom. We'll have different types of soldiers, like Warriors, Wizards, Archers, etc. They will each have different attack strategies.

Shubh: Oh nice! You're creating a very interesting game. I hope it becomes popular.

You: Thanks! But here's the problem: at runtime (when the game is running), how will a character know their attack strategy? Should I write a bunch of if-else conditions (e.g., if a character is an archer, then shoot arrows; else if a character is a warrior, then punch, etc.)?

Shubh: That's definitely not a good idea! It might solve the problem now, but if there are hundreds of different kinds of characters, your code will become unnecessarily huge.

Shubh: Also, imagine if you have to write the same logic in another file. You would end up writing those if-else statements again.

You: Yeah, I've thought about that. Another way I can think of is to create an interface Character and implement it with Warrior, Wizard, and Archer, each having its own behavior for the attack() method.

Shubh: This would violate the Interface Segregation principle of SOLID principles. In the future, if you introduce a fly() method in Character, then all implementations like Warrior and Archer would have to implement it, even if they don't need it. That's not good.

Shubh: Another problem is if you have four implementations: Warrior, Wizard, Archer, and Commander, and Warrior and Commander have the same attack strategy. Remember the DRY principle (Don't Repeat Yourself). We're violating it here by writing the same behavior in both Warrior and Commander.

You: Is there a better way to handle this?

Shubh: The Strategy Design Pattern is the way to solve this issue. It lets you inject the algorithm at runtime. For example, inject a punching algorithm for a warrior at runtime, inject a shooting arrows algorithm for an archer, and so on.

You: Wow! Can you explain this using a UML diagram?

Shubh: Sure.

UML​

Strategy Design Pattern

UML for Strategy Design Pattern

Shubh: In the UML diagram, we have:

  1. Strategy Interface: This defines a method attack() that various strategies will implement, such as melee, spell, and ranged attacks.

  2. Concrete Strategies: Classes like MeleeAttack, SpellAttack, and RangedAttack implement the AttackStrategy interface, providing specific behaviors for each type of attack.

  3. Context (Character): The Character class has a hasA relationship with AttackStrategy, meaning it can hold a reference to an AttackStrategy object. This allows it to change its behavior at runtime by using the setAttackStrategy(AttackStrategy attackStrategy) method. Whenever you call attack(), the character will attack according to the currently set strategy.

Code Implementation​

AttackStrategy Interface​

AttackStrategy.java
public interface AttackStrategy {
void attack();
}

MeleeAttack Class​

MeleeAttack.java
class MeleeAttack implements AttackStrategy {
@Override
public void attack() {
System.out.println("Performing a melee attack!");
}
}

SpellAttack Class​

SpellAttack.java
class SpellAttack implements AttackStrategy {
@Override
public void attack() {
System.out.println("Casting a spell!");
}
}

RangedAttack Class​

RangedAttack.java
class RangedAttack implements AttackStrategy {
@Override
public void attack() {
System.out.println("Performing a ranged attack!");
}
}

Character Class​

Character.java
class Character {
private AttackStrategy strategy;

public void setStrategy(AttackStrategy strategy) {
this.strategy = strategy;
}

public void attack() {
strategy.attack();
}
}

Main Class (Client Code)​

Main.java
public class Main {
public static void main(String[] args) {
Character character = new Character();

// Warrior uses melee attack
character.setStrategy(new MeleeAttack());
character.attack();

// Mage uses spell attack
character.setStrategy(new SpellAttack());
character.attack();

// Archer uses ranged attack
character.setStrategy(new RangedAttack());
character.attack();
}
}

Key Benefits of the Strategy Design Pattern​

Before watching, make sure you give it a try
  1. Flexibility: The Strategy Pattern allows you to change the behavior of a class by switching its strategy at runtime. This makes your code more flexible and adaptable to changes.

  2. Single Responsibility Principle: The Strategy Pattern adheres to this principle by allowing you to encapsulate algorithms or behaviors in separate classes, making your code more modular.

  3. Open/Closed Principle: The Strategy Pattern makes it easy to add new strategies without modifying existing code. You can introduce new behaviors by simply creating new strategy classes.

  4. Avoids Conditional Statements: By using the Strategy Pattern, you can avoid complex if-else or switch-case statements, making your code cleaner and easier to understand.

  5. Reusability: Strategies can be reused across different contexts. For example, the MeleeAttack strategy can be used by both Warrior and Commander, promoting code reuse and reducing redundancy.