Design Pattern

전략패턴

우드의개발개발 2023. 7. 24. 14:41

선행 개념

  • 의존관계
    • 객체와 객체 간 관계의 지속기간이 짧은 경우
      • 특정 클래스 내부 메서드에서 인자로 특정 객체를 인자로 받을 때
        • 위의 상황에서 객체의 지속 기간이 짧은 이유는 해당 메서드를 사용할 때 마다 필요한 객체를 사용하고 역할이 끝나면 객체를 사용한 객체와 연관성이 없기 때문
  • 연관 관계
    • 객체와 객체 간 관계의 지속기간이 긴 경우 
      • 특정 클래스의 생성자 메서드에서 인자로 특정 객체를 받을 때
        • 위의 상황에서 객체의 지속 기간이 긴 이유는 생성자 메서드에서 해당 객체를 생성 후 해당 객체를 계속해서 가지고 있기 때문
  • 전체 객체
    • 다른 객체의 기능을 사용하는 객체
  • 부분객체
    • 전체 객체에게 사용되는 객체
  • 집합 관계의 종류
    • 집약 관계
      • 한 객체가 다른 객체를 포함 할 때
      • 부분객체를 포함한다
      • 전체 객체의 라이프타임과 부분 객체의 라이프타임은 독립적이다
    • 합성관계
      • 부분객체게 전체 객체에 속한다
      • 전체 객체가 사라지면 부분객체도 사라진다

전략패턴의 구성

  • Context 객체
  • Strategy 인터페이스
  • Strategy 객체

 

Context 객체

  • 변수
    • strategy
      • Strategy 인터페이스를 구현한 Strategy 객체를 받는 변수
  • 메서드
    • setStrategy
      • 해당 메서드는 strategy 변수에 Strategy 인터페이스를 구현한 Strategy 객체를 할당한다.

Strategy Interace

  • 메서드
    • execute
      • Strategy Interface를 구현한 메서드. Strategy 객체 별로 다른 로직이 구현된다.

전략 패턴이란?

클라이언트에서 전략 중 하나를 선택하여 Context 클래스 생성시 지정하여 사용함으로써 원하는 전략을 필요할 때 사용할 수 있어 다양한 전략을 구상할 수 있다. Strategy 인터페이스를 사용하는 Strategy Class를 생성할 때 비슷한 범주로 묶을 수 있어야한다. 예를 들어 정렬과 관련한 Strategy 인터페이스를 선언한 경우 해당 인터페이스를 구현한 Strategy 클래스는 정렬을 구현하는 카테고리로 묶을 수 있다. 정렬과 다른 다른 전략이 필요 할 경우 다른 인터페이스를 만들어서 카테고리를 다시 나누어야 한다.

전략 패턴 장점

  • 행동 패턴의 일종
  • 원하는 전략 객체를 지정할 수 있어 갈아끼울 수 있다.
    • 캡슐화
    • 재사용성
  • 테스트하기 용이함

전략 패턴 단점

  • 전략 인터페이스를 구상한 전략 클래스의 개수가 늘어나 관리가 필요하다.
  • 전략 클래스가 많으면 선택하기 까다롭다.
  • 과도한 객체의 생성은 퍼포먼스에 영향을 미칠 수 있다.

파이썬 코드

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import List


class Context():
    """
    The Context defines the interface of interest to clients.
    """

    def __init__(self, strategy: Strategy) -> None:
        """
        Usually, the Context accepts a strategy through the constructor, but
        also provides a setter to change it at runtime.
        """

        self._strategy = strategy

    @property
    def strategy(self) -> Strategy:
        """
        The Context maintains a reference to one of the Strategy objects. The
        Context does not know the concrete class of a strategy. It should work
        with all strategies via the Strategy interface.
        """

        return self._strategy

    @strategy.setter
    def strategy(self, strategy: Strategy) -> None:
        """
        Usually, the Context allows replacing a Strategy object at runtime.
        """

        self._strategy = strategy

    def do_some_business_logic(self) -> None:
        """
        The Context delegates some work to the Strategy object instead of
        implementing multiple versions of the algorithm on its own.
        """

        # ...

        print("Context: Sorting data using the strategy (not sure how it'll do it)")
        result = self._strategy.do_algorithm(["a", "b", "c", "d", "e"])
        print(",".join(result))

        # ...


class Strategy(ABC):
    """
    The Strategy interface declares operations common to all supported versions
    of some algorithm.

    The Context uses this interface to call the algorithm defined by Concrete
    Strategies.
    """

    @abstractmethod
    def do_algorithm(self, data: List):
        pass


"""
Concrete Strategies implement the algorithm while following the base Strategy
interface. The interface makes them interchangeable in the Context.
"""


class ConcreteStrategyA(Strategy):
    def do_algorithm(self, data: List) -> List:
        return sorted(data)


class ConcreteStrategyB(Strategy):
    def do_algorithm(self, data: List) -> List:
        return reversed(sorted(data))


if __name__ == "__main__":
    # The client code picks a concrete strategy and passes it to the context.
    # The client should be aware of the differences between strategies in order
    # to make the right choice.

    context = Context(ConcreteStrategyA())
    print("Client: Strategy is set to normal sorting.")
    context.do_some_business_logic()
    print()

    print("Client: Strategy is set to reverse sorting.")
    context.strategy = ConcreteStrategyB()
    context.do_some_business_logic()

'Design Pattern' 카테고리의 다른 글

데코레이터 패턴  (0) 2023.07.25
옵저버 패턴  (0) 2023.07.24
추상 팩토리 패턴  (0) 2023.06.09