A A
[Design Pattern] Strategy Pattern - ์ „๋žต ํŒจํ„ด

Strategy Pattern

์ŠคํŠธ๋ž˜ํ‹ฐ์ง€ ํŒจํ„ด(Strategy Pattern)์€ ํ–‰์œ„ ํŒจํ„ด(Behavioral Pattern)์˜ ์ผ์ข…์œผ๋กœ, ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฐ์„ ์ •์˜ํ•˜๊ณ , ๊ฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์บก์Šํ™”ํ•˜๋ฉฐ, ์ด๋“ค์„ ์ƒํ˜ธ ๊ตํ™˜ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
  • ์ฆ‰, ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ถ„๋ฆฌํ•˜์—ฌ ๋…๋ฆฝ์ ์œผ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ด๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์˜ ๋ณ€๊ฒฝ ์—†์ด ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‰ฝ๊ฒŒ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Runtime ์ค‘์— ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์„ ํƒํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•˜๋‚˜์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ง์ ‘ Implementing ํ•˜๋Š” ๋Œ€์‹  ์–ด๋– ํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ• ์ง€ Runtime Instruction์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  • Animal์„ ๋ฐ›์•„์„œ Speaking์„ ์‹œํ‚ค๋Š” ํ•จ์ˆ˜
  • ๋™๋ฌผ์€ ๊ณ ์–‘์ด ํ˜น์€ ์‚ฌ์ž
  • ๊ณ ์–‘์ด๊ฐ€ ๋“ค์–ด์˜ค๋ฉด “meow”
  • ์‚ฌ์ž๊ฐ€ ๋“ค์–ด์˜ค๋ฉด “roar”
  • ํ•จ์ˆ˜๋Š” ์ด ์ƒํƒœ๋กœ ๊ณ ์ •์ด ๋œ ์ƒํƒœ์ด๋ฉฐ ์–ด๋–ค ๋™๋ฌผ์ด ์–ด๋–ค๋ง์„ ํ•˜์ง€๋Š” Runtime์— ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.

 

class Animal:
    def speak(self):
        pass

class Cat(Animal):
    def speak(self):
        print("meow")

class Lion(Animal):
    def speak(self):
        print("roar")

def makeSpeak(animal: Animal):
    animal.speak()

def createAnimal(input_str: str) -> Animal:
    if input_str == "cat":
        return Cat()
    elif input_str == "lion":
        return Lion()
  • Animal class
    • Base Interface
    • speak ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • Cat class
    • Animal ์ƒ์†๋ฐ›๊ณ 
    • speak๋ฅผ ํ†ตํ•ด "meow" ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • Lion class
    • Animal ์ƒ์†๋ฐ›์Œ
    • speak๋ฅผ ํ†ตํ•ด "roar" ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • makeSpeak()
    • argument๋กœ Animal Interface ๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค.
    • Animal.speak()๋ฅผ ํ†ตํ•ด ๋ง์„ ํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • createAnimal()
    • argument๋กœ string ํ˜•ํƒœ์˜ input_str ๋ฐ›๊ณ  Animal object return ํ•ฉ๋‹ˆ๋‹ค.
    • input_str์ด "cat"์ผ ๊ฒฝ์šฐ Cat object return
    • input_str์ด "lion"์ผ ๊ฒฝ์šฐ Lion object return

 

# ์‚ฌ์šฉ ์˜ˆ์‹œ
input_str = input('choose animal: ')

animal = createAnimal(input_str)
makeSpeak(animal)

# ์‹คํ–‰ ๊ฒฐ๊ณผ
# choose animal: cat
# meow
# choose animal: lion
# roar
  • ์‚ฌ์šฉ ์˜ˆ์‹œ
    • Runtime์— input์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๋ณ€์ˆ˜์ธ input_str
    • createAnimal์„ ํ†ตํ•ด input_str์— ๋”ฐ๋ผ animal์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    • ๊ทธ ๋‹ค์Œ animal์—๊ฒŒ makeSpeak์„ ํ†ตํ•ด ๋ง์„ ๊ฒ๋‹ˆ๋‹ค.
    • ์ƒ๋‹จ ์ฝ”๋“œ ์‹คํ–‰์‹œํ‚ค๋ฉด Runtime ์ค‘์— ์–ด๋–ค ๋™๋ฌผ์„ ๋ฐ›์„์ง€ ๋ฌผ์–ด๋ด…๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ณผ
    • input_str์ด "cat"์ด๋ฉด "meow"
    • input_str์ด "lion"์ด๋ฉด "roar"

  • ๋Ÿฐํƒ€์ž„ ์ค‘์— ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์„ ํƒํ•˜์—ฌ ๊ฐ์ฒด ๋™์ž‘์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ „๋žต์ด๋ž€ ๊ธฐ๋Šฅ, ๋™์ž‘, ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋“ฑ ํŠน์ •ํ•œ ๋ชฉํ‘œ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ํ–‰๋™์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ณ€๊ฒฝ์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

 

  • Strategy: ์ „๋žต์„ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•œ APi๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ConcreteStrategy: API๋ฅผ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•˜๋ฉฐ, ์—ฌ๊ธฐ์„œ ๊ตฌ์ฒด์ ์ธ ์ „๋žต (์ž‘์ „, ๋ฐฉ์ฑ…, ๋ฐฉ๋ฒ•, ์•Œ๊ณ ๋ฆฌ์ฆ˜)์„ ์‹ค์ œ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•ฉ๋‹ˆ๋‹ค.
  • Context: Strategy๋ฅผ ๋‹ด์•„๋‚ด๊ณ  ์ด์šฉํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š”๋ฐ, ConcreteStrategy์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ ํ•„์š”์— ๋”ฐ๋ผ ๊ทธ๊ฒƒ์„ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค.

 

from abc import *

class IStrategy(metaclass=ABCMeta):
    @abstractmethod
    def doSomething(self):
        pass

class ConcreteStrategyA(IStrategy):
    def doSomething(self):
        print("do something using A")

class ConcreteStrategyB(IStrategy):
    def doSomething(self):
        print("do something using B")

class Context:
    def __init__(self):
        self.strategy = 0  # ์ถ”์ƒํ™”๋œ ์ „๋žต์„ ๊ฐ€์ง€๊ณ  ์žˆ์Œ

    def setStrategy(self, stgy: IStrategy):
        self.strategy = stgy  # ์„ค์ •๋œ ์ „๋žต์— ๋”ฐ๋ผ ํ–‰๋™ํ•จ

    def do(self):
        self.strategy.doSomething()

# ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ
context = Context()
context.setStrategy(ConcreteStrategyA())
context.do()
context.setStrategy(ConcreteStrategyB())
context.do()
  • ์ „๋žต ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์—ฌ๋Ÿฌ ๋ฒ„์ „ ๋˜๋Š” ๋ณ€ํ˜•์ด ํ•„์š”ํ•  ๋•Œ ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์ฝ”๋“œ๊ฐ€ ๋…ธ์ถœ ๋˜์–ด์„œ๋Š” ์•ˆ๋˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๋™์ž‘์ด ๋Ÿฐํƒ€์ž„์— ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ต์ฒด๋˜์–ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Strategy Pattern์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์‚ฌ๋ก€

weapon = {
    "SWORD": 0,
    "SHIELD": 1,
    "CROSSBOW": 2
}

class TakeWeapon:
    def __init__(self):
        self.state = 0

    def setState(self, state):
        self.state = state

    def attack(self):
        if self.state == weapon["SWORD"]:
            print("๊ฒ€์„ ํœ˜๋‘๋ฅด๋‹ค")
        elif self.state == weapon["SHIELD"]:
            print("๋ฐฉํŒจ๋กœ ๋ฐ€์น˜๋‹ค")
        elif self.state == weapon["CROSSBOW"]:
            print("์„๊ถ์„ ์˜๋‹ค")

# ํ”Œ๋ ˆ์ด์–ด ์†์— ๋ฌด๊ธฐ ์ฐฉ์šฉ ์ „๋žต์„ ์„ค์ •
hand = TakeWeapon()

# ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๊ฒ€์„ ๋“ค๋„๋ก ์ „๋žต ์„ค์ •
hand.setState(weapon["SWORD"])
hand.attack()  # "๊ฒ€์„ ํœ˜๋‘๋ฅด๋‹ค"

# ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๋ฐฉํŒจ๋ฅผ ๋“ค๋„๋ก ์ „๋žต ๋ณ€๊ฒฝ
hand.setState(weapon["SHIELD"])
hand.attack()  # "๋ฐฉํŒจ๋กœ ๋ฐ€์น˜๋‹ค"

# ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์„๊ถ์„ ๋“ค๋„๋ก ์ „๋žต ๋ณ€๊ฒฝ
hand.setState(weapon["CROSSBOW"])
hand.attack()  # "์„๊ถ์„ ์˜๋‹ค"
  • TakeWeapon ํด๋ž˜์Šค๋Š” ํ•˜์œ„ ๋ฉ”์„œ๋“œ(ํ•จ์ˆ˜)์— ์˜์กด๋˜์–ด ์žˆ์œผ๋ฉฐ, ๊ณต๊ฒฉ ๋ฐฉ๋ฒ•์ด ์ถ”๊ฐ€๋˜๋ฉด TakeWeapon ํด๋ž˜์Šค๋„ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋ฏ€๋กœ if else ๊ตฌ๋ฌธ์ด ๋งŽ์•„์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Strategy Pattern์„ ์‚ฌ์šฉํ•œ ์‚ฌ๋ก€

# Strategy pattern implementation using DIP and polymorphism
class TakeWeaponStrategy:
    def __init__(self):
        self.weapon = 0

    def setWeapon(self, weapon: Weapon):
        self.weapon = weapon

    def attack(self):
        self.weapon.offensive()

# Abstract Weapon class
from abc import *

class Weapon(metaclass=ABCMeta):
    @abstractmethod
    def offensive(self):
        pass

# Concrete Weapon classes implementing the abstract Weapon class
class Sword(Weapon):
    def offensive(self):
        print("์นผ์„ ํœ˜๋‘๋ฅด๋‹ค")

class Shield(Weapon):
    def offensive(self):
        print("๋ฐฉํŒจ๋กœ ๋ฐ€์น˜๋‹ค")

class CrossBow(Weapon):
    def offensive(self):
        print("์„๊ถ์„ ์˜๋‹ค")

# Setting the strategies for the player
hand = TakeWeaponStrategy()
hand.setWeapon(Sword())
hand.attack()  # ์นผ์„ ํœ˜๋‘๋ฅด๋‹ค
hand.setWeapon(Shield())
hand.attack()  # ๋ฐฉํŒจ๋กœ ๋ฐ€์น˜๋‹ค
hand.setWeapon(CrossBow())
hand.attack()  # ์„๊ถ์„ ์˜๋‹ค
  • Dependency Inversion Principle(DIP) ๋ฐ ๋‹คํ˜•์„ฑ์„ ํ†ตํ•œ ์‹ค์‹œ๊ฐ„ ๊ณต๊ฒฉ ์ „๋žต์˜ ๊ต์ฒด๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • TakeWeaponStrategy ํ•จ์ˆ˜ ์ˆ˜์ •์ด ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค.

์‹คํ–‰์ค‘์— ๊ต์ฒด๋„ ๊ฐ€๋Šฅ

  • Strategy ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ํ”„๋กœ๊ทธ๋žจ์˜ ๋™์ž‘ ์ค‘์— ConcreteStrategy ์—ญํ• ์˜ ํด๋ž˜์Šค๋ฅผ ๊ต์ฒดํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ€๋ น, ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ ์€ ํ™˜๊ฒฝ์—์„œ๋Š” Slow but Less Memory Strategy๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋งŽ์€ ํ™˜๊ฒฝ์—์„œ๋Š” Fast but More Memory Strategy๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ์ƒ๊ฐํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋˜๋Š” ๋ณต์žกํ•œ ๊ณ„์‚ฐ์ด ํ•„์š”ํ•œ ํƒœ์Šคํฌ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•  ๋•Œ,
    • ๋ฒ„๊ทธ๊ฐ€ ์žˆ์„์ง€๋„ ๋ชจ๋ฅด๋Š” ๊ณ ์†์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜ vs ์ €์†์ด์ง€๋งŒ ํ™•์‹คํ•œ ๊ณ„์‚ฐ์„ ์‹คํ–‰ํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ž…๋‹ˆ๋‹ค.

์ผ๋ถ€๋Ÿฌ Strategy Pattern์„ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์žˆ์„๊นŒ?

๋ณดํ†ต ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•  ๋•Œ ๋ฉ”์†Œ๋“œ ๋‚ด๋ถ€์— ๋™ํ™”๋œ ํ˜•ํƒœ๋กœ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
  • ๊ทธ๋Ÿฌ๋‚˜, Strategy ํŒจํ„ด์—์„œ๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๋ถ€๋ถ„์„ ๋‹ค๋ฅธ ๋ถ€๋ถ„๊ณผ ์˜์‹์ ์œผ๋กœ ๋ถ„๋ฆฌํ•ด์„œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ API๋ถ€๋ถ„๋งŒ์„ ๊ณ ์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ , ํ”„๋กœ๊ทธ๋žจ์—์„œ ์œ„์ž„์— ์˜ํ•ด ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์ด๊ฒƒ์€ ํ”„๋กœ๊ทธ๋žจ์„ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ฐœ๋Ÿ‰ํ•ด์„œ ์กฐ๊ธˆ ๋” ๋น ๋ฅด๊ฒŒ ํ•˜๊ณ  ์‹ถ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด๋ฉด, Strategy ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด Strategy ์—ญํ• ์˜ API๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•˜๊ณ  ConcreteStrategy์˜ ์—ญํ• ๋งŒ์„ ์ˆ˜์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • ๋”์šฑ์ด ์œ„์ž„์ด๋ผ๋Š” ๋Š์Šจํ•œ ์—ฐ๊ฒฐ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์šฉ์ดํ•˜๊ฒŒ ๊ตํ™˜ํ•  ์ˆ˜ ์žˆ๊ณ , ๊ฐ€๋ น ์›๋ž˜์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ ๊ฐœ์„ ํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์†๋„๋ฅผ ๋น„๊ตํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์—๋„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ต์ฒดํ•ด์„œ ์‹œํ—˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.