선행 개념
- 의존관계
- 객체와 객체 간 관계의 지속기간이 짧은 경우
- 특정 클래스 내부 메서드에서 인자로 특정 객체를 인자로 받을 때
- 위의 상황에서 객체의 지속 기간이 짧은 이유는 해당 메서드를 사용할 때 마다 필요한 객체를 사용하고 역할이 끝나면 객체를 사용한 객체와 연관성이 없기 때문
- 특정 클래스 내부 메서드에서 인자로 특정 객체를 인자로 받을 때
- 객체와 객체 간 관계의 지속기간이 짧은 경우
- 연관 관계
- 객체와 객체 간 관계의 지속기간이 긴 경우
- 특정 클래스의 생성자 메서드에서 인자로 특정 객체를 받을 때
- 위의 상황에서 객체의 지속 기간이 긴 이유는 생성자 메서드에서 해당 객체를 생성 후 해당 객체를 계속해서 가지고 있기 때문
- 특정 클래스의 생성자 메서드에서 인자로 특정 객체를 받을 때
- 객체와 객체 간 관계의 지속기간이 긴 경우
- 전체 객체
- 다른 객체의 기능을 사용하는 객체
- 부분객체
- 전체 객체에게 사용되는 객체
- 집합 관계의 종류
- 집약 관계
- 한 객체가 다른 객체를 포함 할 때
- 부분객체를 포함한다
- 전체 객체의 라이프타임과 부분 객체의 라이프타임은 독립적이다
- 합성관계
- 부분객체게 전체 객체에 속한다
- 전체 객체가 사라지면 부분객체도 사라진다
- 집약 관계
전략패턴의 구성
- Context 객체
- Strategy 인터페이스
- Strategy 객체
Context 객체
- 변수
- strategy
- Strategy 인터페이스를 구현한 Strategy 객체를 받는 변수
- strategy
- 메서드
- setStrategy
- 해당 메서드는 strategy 변수에 Strategy 인터페이스를 구현한 Strategy 객체를 할당한다.
- setStrategy
Strategy Interace
- 메서드
- execute
- Strategy Interface를 구현한 메서드. Strategy 객체 별로 다른 로직이 구현된다.
- execute
전략 패턴이란?
클라이언트에서 전략 중 하나를 선택하여 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 |