설계패턴 19. State Pattern

LSDrug·2024년 6월 12일

설계패턴(完)

목록 보기
19/26

1. 정의

state는 상태를 의미한다. 상태를 클래스로 표현하면 클래스를 교체해서 '상태의 변화'를 표현할 수 있다.

State 패턴은 상태를 하나의 클래스로 제작하여, 클래스에 따라 변화하는 상태를 표시한 패턴이다.

Strategy Pattern과 유사한 점을 띠고 있다.

해당 패턴은 우리가 만들고자 하는 객체에 상태가 있다면 사용할 수 있는 패턴이다.

  • 예) 신호등 - 녹색, 빨강색 중 하나의 상태만 가지며 계속해서 상태가 변한다.

2. 개념 확장

Divide and conquer

  • 주어진 문제를 작은 문제들로 나누는 것.

  • 각각의 구체적인 상태를 각각의 클래스로 표현해서 문제를 분할한다.

  • State 패턴을 사용하지 않으면 상태가 많을수록 조건분기도 증가한다. 이는 사건이 발생할 때마다 호출되는 메소드에 조건분기를 기술해야 하는 치명적인 단점이 생긴다는 것이다.

--> State 패턴을 이용해 시스템 상태를 클래스로 분할, 표현해 효율적으로 나타낼 수 있다.

State의 전환은 누가 관리해야 하는가?

  • 상태를 클래스로 표현해서 ConcreteState 역할에 분담하는 것은 좋은 방법이다. 그러나 State 패턴을 사용할 경우 상태 전환은 누가 관리해야 하는지 생각해 봐야 한다.

  • 신호등을 일례로 보면
    Context 역할의 Traffic Light가 상태 전환을 수행하도록 setState를 구현하였다. 그러나 setState를 실제로 호출하는 것은 ConcreteState 역할의 GreenLight 또는 RedLight 클래스이다.

  • 즉, '상태 전환'을 '상태에 의존한 동작'으로 간주되고 있는데 이것은 장단점이 있다.

장점: 다른 상태로 전환하는 것에 대한 정보가 하나의 클래스 내에 정리되어 있고, 가령 GreenLight 클래스가 어떤 상태로 전환하는지는 GreenLight 클래스 코드만 읽으면 된다. -> 상태 전환을 알기 쉽고 대응하기 쉽다.'

단점: 하나의 ConcreteState 역할이 다른 ConcreteState 역할을 서로 알아야 한다. 만약, 하나를 삭제하고 싶으면 의존된 또 다른 것도 수정해야 한다.

=> 상태 전환을 ConcreteState에게 맡기면 클래스 사이의 의존관계가 깊어진다.

Context 역할에게 상태전환의 업무를 맡기면 되지 않을까?

ConcreteState 역할의 독립성이 높아지는 것은 좋다. 그러나 이번에는 Context 역할이 모든 ConcreteState 역할을 알아야 하는 문제가 발생한다.

3. 예시

신호등을 해당 패턴을 통해 만들어 보자.

class TrafficLight:
    def __init__(self):
        # 기본 state로 GreenLight 가짐
        self.state = GreenLight()

    def setState(self, state):
        # argument로 받은 state로 변경
        self.state = state

    def speak(self):
        # 현재 상태 object의 speak 함수인 status() 호출
        # 초록 불이라면 greenLight class의 status() 호출
        # 빨강 불이라면 redLight class의 status() 호출
        self.state.status()

    def wait(self):
        # speak와 마찬가지로 현재 상태 object의 wait함수인 
        # changeLight()호출
        self.state.changeLight(self)


class State:
    def status(self):
        # 현재 상태 출력
        pass
    def changeLight(self, state):
        # argument로 받은 state로 변경
        pass


class GreenLight(State):
    def status(self):
        # 자신의 상태 출력
        print('green light')

    def changeLight(self, trafficLight: TrafficLight):
        # 상태 변경해주는 함수
        # TrafficLight의 setState를 통해서 상태 변경해야 됨
        # TrafficLight의 setState를 호출하기 위해서는 
        # argument TrafficLight을 받아야 함
        trafficLight.setState(RedLight())


class RedLight(State):
    def status(self):
        print('red light')

    def changeLight(self, trafficLight: TrafficLight):
        trafficLight.setState(GreenLight())


t_light = TrafficLight()
t_light.speak() # green light
t_light.wait() # wait.. The light changed
t_light.speak() # red light
profile
마약같은 코딩, 마약같은 코딩러

0개의 댓글