본문 바로가기

취업과 기본기 튼튼/빽 투더 기본기

[디자인 패턴 13편] 행동 패턴, 상태 (State)

1. 개념

상태 패턴은 상태 자체를 객체화함으로써,
상태에 따른 액션도 상태 객체에 내부에 구현하는 패턴이다.

덧붙이면, 보통 객체를 추상화할 때 행동의 주체가 클래스, 대상이 하는 행동이 메쏘드로 정의되고,해당 대상의 상태는 속성으로 정의된다. 따라서 현재 주체의 상태에 따라 행동이 다를 경우, 상태에 조건문을 두어 행동을 다르게 하는 경우가 많다.

그런데 이러면 상태의 종류가 많아질수록 조건문도 많아지게 되고, 코드의 가독성이 떨어지게 된다는 단점이 있다.
이를 보완하고자, 상태 자체를 객체로 만들고 상태에 따른 액션도 이 객체에다가 포함시킨다.
이러면, 주체는 상태를 일일이 조건문으로 검사하지 않고, 올바른 상태 객체의 액션만 실행시키면 원래 원하던 일을 수행할 수 있다.

1.1. 구조

  • State
    • 상태를 나타내는 추상 클래스.
      • ex. class LightState 이런 식으로 상태를 추상화 함.
    • 각 상태에 따른 액션(메쏘드) 인터페이스를 정의한다.
      • 같은 계열의 상태이므로, 각 상태에 따른 동일한 인터페이스(메쏘드)를 가지고 있다.
      • 각 액션은 handle() 로 표현되며 ConcreteState 클래스에서 이를 구현한다.
  • ConcreteState
    • State 를 상속받아 구체적인 상태를 나타내는 클래스
      • ex. class ON(LightState), class OFF(LightState) 이런 식으로 구체적인 상태를 표현.
    • handle 를 구체적으로 구현한다.

1.2. 장점

  • 상태 종류 증가에 따른 길어지는 조건문을 제거하고, 가독성 좋게 코드를 짤 수 있다.
  • 상태 - 행위를 묶기 때문에, 주체(클래스) 자체는 상태에 따른 행동 구현과 분리된다.

1.3. 단점

  • 상태 종류에 따라 클래스가 늘어난다.
  • 상태와 행동에 강력한 결합이 생긴다. (이게 장점이자 단점인 거 같다. 패턴을 잘보고 사용해야 할듯 .)

1.4. 활용 상황

  • 주체(클래스) 에서 동일 계열의 상태들에 따라 조건문이 많아지는 경우
  • 이런 경우가 여러 클래스에서 등장하는 경우
  • 즉 이런 조건문 반복 코드가 여기저기서 발견될 때, 차라리 객체로 묶어버리는게 낫다는 것.

2. 구현

클라이언트는 다음과 같이 사용할 수 있다.

context = Context()

# 객체 상태를 ConcreteStateA 로 설정 후 액션 수행
context.state = ConcreteStateA()
context.request()

# 객체 상태를 ConcreteStateB 로 설정 후 액션 수행
context.state = ConcreteStateB()
context.request()

클라이언트가 사용하는 객체 Context 는 다음과 같이 생겼다.

class Context:
  def __init__(self):
    self.state = None

  def request(self):
    self.state.handle()

State 들을 정의하는 추상 클래스 State 는 다음과 같다.

class State(metaclass=ABCMeta):
  @abstractmethod
  def handle(self):
    pass

State 를 상속받아 구체적인 상태 객체를 정의하는 ConcreteState 들은 다음과 같다.

class ConcreteStateA(State):
  def handle(self):
    pass

class ConcreteStateB(State):
  def handle(self):
    pass

3. 참고