Featured image of post BP2 - Strategy - Learn Design Pattern From Simple Things

BP2 - Strategy - Learn Design Pattern From Simple Things

As the coach of a strong team, you plan to attack to find a goal and then prioritize possession to preserve the score. This combination is called strategy.

Strategy is a behavioral design pattern: šŸ–¤šŸ–¤šŸ–¤šŸ–¤šŸ–¤

What is Strategy?

Strategy is a technique to be able to insert a new logic/plugin TikiTaka, CounterAttack into a main object FootballClub dynamically.

Strategy diagram

Why use Strategy?

  • Defining new logic/plugin will not change the code of the main object.
  • The new logic/plugin will only be plugged in as needed, make the code more comprehensible.

Question: I hate the Strategy, what are the alternatives?

Answer: You can create multiple logic/plugins counter_attack_pass_the_ball, tiki_taka_pass_the_ball directly in main object instead of Plugins, but it’s not pretty!

NoStrategy

When to use Strategy?

Question: When do I use Strategy?

Answer: When you have multiple logic/plugins TikiTaka, CounterAttack for an object and logic/plugins usage changes over time.

Input:

  • Having a main objectFootballClub:
    • There is a main feature attack with multiple logic/plugins TikiTaka, CounterAttack being swapped out for each situation šŸ”„.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
"""
class FootballClub:
    def attack(self):
        formation = [
            ["LB", "CB", "CB", "CB"],
            ["LCM", "CDM", "RCM"],
            ["LW", "CF", "RW"],
        ]
        ...pass_the_ball...
"""

import random
from abc import ABC, abstractmethod


class Strategy(ABC):
    name = ""

    @abstractmethod
    def pass_the_ball(self, formation):
        pass


class TikiTaka(Strategy):
    name = "TikiTaka"

    def pass_the_ball(self, formation):
        defenders = formation[0]
        midfielder_forwards = [*formation[1], *formation[2]]

        random.shuffle(defenders)
        random.shuffle(midfielder_forwards)
        return [*defenders, *midfielder_forwards]


class CounterAttack(Strategy):
    name = "CounterAttack"

    def pass_the_ball(self, formation):
        defenders = random.choice(formation[0])
        midfielders = random.choice(formation[1])
        forwards = random.choice(formation[2])
        return [defenders, midfielders, forwards]

Expected Output:

  • The main object FootballClub has the ability to dynamically run different pass_the_ball logic/plugins at different times:
    • at the start: pass_the_ball in CounterAttack style.
    • at the end: pass_the_ball in TikiTaka style.
1
2
3
4
Manager: Hey team! apply CounterAttack
LB >> CDM >> RW >> GOAAAALLL!!!
Manager: Hey team! apply TikiTaka
CB >> CB >> LB >> CB >> CDM >> RCM >> RW >> LW >> LCM >> CF >> GOAAAALLL!!!

How to implement Strategy?

Non-Strategy implementation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class FootballClub:
    def __init__(self, strategy):
        self._strategy = strategy

    @property
    def strategy(self):
        return self._strategy

    @strategy.setter
    def strategy(self, strategy):
        self._strategy = strategy

    def attack(self):
        formation = [
            ["LB", "CB", "CB", "CB"],
            ["LCM", "CDM", "RCM"],
            ["LW", "CF", "RW"],
        ]
        result = []
        if self._strategy == "TikiTaka":
            result = self.tiki_taka_pass_the_ball(formation)
        elif self._strategy == "CounterAttack":
            result = self.counter_attack_pass_the_ball(formation)
        result.append("GOAAAALLL!!!")
        print(" >> ".join(result))

    def tiki_taka_pass_the_ball(self, formation):
        defenders = formation[0]
        midfielder_forwards = [*formation[1], *formation[2]]

        random.shuffle(defenders)
        random.shuffle(midfielder_forwards)
        return [*defenders, *midfielder_forwards]

    def counter_attack_pass_the_ball(self, formation):
        defenders = random.choice(formation[0])
        midfielders = random.choice(formation[1])
        forwards = random.choice(formation[2])
        return [defenders, midfielders, forwards]


if __name__ == "__main__":
    strategy = "CounterAttack"
    football_club = FootballClub(strategy)
    print(f"Manager: Hey team! apply {strategy}")
    football_club.attack()

    strategy = "TikiTaka"
    print(f"Manager: Hey team! apply {strategy}")
    football_club.strategy = strategy
    football_club.attack()

Strategy Implementation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class FootballClub:
    def __init__(self, strategy):
        self._strategy = strategy

    @property
    def strategy(self):
        return self._strategy

    @strategy.setter
    def strategy(self, strategy):
        self._strategy = strategy

    def attack(self):
        formation = [
            ["LB", "CB", "CB", "CB"],
            ["LCM", "CDM", "RCM"],
            ["LW", "CF", "RW"],
        ]
        result = self._strategy.pass_the_ball(formation)
        result.append("GOAAAALLL!!!")
        print(" >> ".join(result))


if __name__ == "__main__":
    strategy = CounterAttack()
    football_club = FootballClub(strategy)
    print(f"Manager: Hey team! apply {strategy.name}")
    football_club.attack()

    strategy = TikiTaka()
    print(f"Manager: Hey team! apply {strategy.name}")
    football_club.strategy = strategy
    football_club.attack()

Source Code

Made with the laziness šŸ¦„
by a busy guy