Menu
Coddy logo textTech

상태 패턴

Coddy Python 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 64개 중 53번째.

상태 패턴(State Pattern)은 객체의 내부 상태가 변경될 때 객체가 행동을 변경할 수 있도록 허용합니다. 객체는 현재 상태에 따라 클래스를 바꾸는 것처럼 보입니다.

신호등을 위한 간단한 상태 클래스들입니다:

class RedState:
    def next_state(self, light):
        print("Red -> Green")
        light.state = GreenState()
    
    def current_color(self):
        return "Red"

class GreenState:
    def next_state(self, light):
        print("Green -> Yellow")
        light.state = YellowState()
    
    def current_color(self):
        return "Green"

class YellowState:
    def next_state(self, light):
        print("Yellow -> Red")
        light.state = RedState()
    
    def current_color(self):
        return "Yellow"

각 상태는 다음 상태로 전이될 때 어떤 일이 일어나는지를 정의합니다.

현재 상태를 유지하는 컨텍스트 클래스를 생성합니다:

class TrafficLight:
    def __init__(self):
        self.state = RedState()  # 빨간색으로 시작
    
    def change(self):
        self.state.next_state(self)
    
    def get_color(self):
        return self.state.current_color()

신호등은 동작을 현재 상태 객체에 위임합니다.

신호등을 사용하세요:

light = TrafficLight()
print(f"Current: {light.get_color()}")

light.change()  # 빨간색 -> 초록색
print(f"Current: {light.get_color()}")

light.change()  # 초록색 -> 노란색
print(f"Current: {light.get_color()}")

light.change()  # 노란색 -> 빨간색
print(f"Current: {light.get_color()}")

간단한 플레이어를 사용한 또 다른 예제를 만들어 보겠습니다:

class PlayingState:
    def play(self, player):
        print("Already playing")
    
    def stop(self, player):
        print("Stopping music")
        player.state = StoppedState()

class StoppedState:
    def play(self, player):
        print("Starting music")
        player.state = PlayingState()
    
    def stop(self, player):
        print("Already stopped")

class MusicPlayer:
    def __init__(self):
        self.state = StoppedState()
    
    def play(self):
        self.state.play(self)
    
    def stop(self):
        self.state.stop(self)

player = MusicPlayer()
player.play()   # 음악 시작
player.play()   # 이미 재생 중
player.stop()   # 음악 정지
player.stop()   # 이미 정지됨

출력:

Current: Red
Red -> Green
Current: Green
Green -> Yellow
Current: Yellow
Yellow -> Red
Current: Red
Starting music
Already playing
Stopping music
Already stopped

핵심 포인트: 상태 패턴(State Pattern)은 상태별 동작을 별도의 클래스에 캡슐화하고 컨텍스트 객체가 현재 상태에 위임하도록 합니다. 상태가 변경되면 동작이 자동으로 변경됩니다. 이는 복잡한 if/else 문을 제거하고 새로운 상태를 추가하는 것을 더 쉽게 만듭니다.

challenge icon

챌린지

쉬움

이 챌린지에서는 적절한 캡슐화와 메서드를 사용하여 state.py 파일에 State 클래스를 구현하게 됩니다. 이 파일에는 구현을 단계별로 안내하기 위한 상세한 TODO 주석이 포함되어 있습니다.

테스트 스위트는 구현이 모든 요구 사항을 충족하고 예외 상황을 올바르게 처리하는지 확인하도록 설계되었습니다. 테스트 결과를 사용하여 예상되는 동작을 이해하고 솔루션을 개선하세요.

치트 시트

스테이트 패턴(State Pattern)은 객체의 내부 상태가 변경될 때 객체가 행동을 변경할 수 있도록 허용합니다. 객체는 현재 상태에 따라 클래스를 바꾸는 것처럼 보입니다.

각 상태에 대한 행동을 정의하는 상태 클래스들을 생성합니다:

class RedState:
    def next_state(self, light):
        print("Red -> Green")
        light.state = GreenState()
    
    def current_color(self):
        return "Red"

class GreenState:
    def next_state(self, light):
        print("Green -> Yellow")
        light.state = YellowState()
    
    def current_color(self):
        return "Green"

현재 상태를 유지하는 컨텍스트(context) 클래스를 생성합니다:

class TrafficLight:
    def __init__(self):
        self.state = RedState()  # 빨간색으로 시작
    
    def change(self):
        self.state.next_state(self)
    
    def get_color(self):
        return self.state.current_color()

사용 예시:

light = TrafficLight()
print(f"Current: {light.get_color()}")  # 현재: Red
light.change()  # Red -> Green
print(f"Current: {light.get_color()}")  # 현재: Green

스테이트 패턴은 상태별 행동을 별도의 클래스로 캡슐화하고, 컨텍스트 객체가 현재 상태에 위임하도록 합니다. 이는 복잡한 if/else 문을 제거하고 새로운 상태를 추가하는 것을 더 쉽게 만듭니다.

직접 해보기

from state import State

# 종합 테스트 케이스 처리기
test_case = input()

if test_case == "basic_test":
    obj = State("Test Name")
    obj.display_info()

elif test_case == "validation_test":
    obj = State("Validation Test")
    print(f"Name: {obj.name}")

elif test_case == "empty_name_test":
    obj = State("")
    obj.display_info()
    print(f"Name property: {obj.name}")

elif test_case == "special_chars_test":
    obj = State("Test@#$%")
    obj.display_info()
    print(f"Name property: {obj.name}")

elif test_case == "long_name_test":
    long_name = "A" * 100
    obj = State(long_name)
    obj.display_info()
    print(f"Name length: {len(obj.name)}")

elif test_case == "multiple_states_test":
    states = [
        State("California"),
        State("Texas"),
        State("New York"),
        State("Florida")
    ]
    
    for state in states:
        state.display_info()
        print(f"Name via property: {state.name}")

elif test_case == "attribute_access_test":
    obj = State("Access Test")
    # 프라이빗 속성에 직접 접근 (권장되지 않음)
    print(f"Direct access (not recommended): {obj._name}")
    # 프로퍼티를 통한 접근 (권장됨)
    print(f"Property access (recommended): {obj.name}")

elif test_case == "property_behavior_test":
    obj = State("Original Name")
    print(f"Original name: {obj.name}")
    
    # 이름 수정을 시도 (세터가 없으므로 실패함)
    try:
        obj.name = "New Name"
    except AttributeError as e:
        print(f"Error when trying to modify name: {e}")
    
    # 이름이 변경되지 않았음을 확인
    print(f"Name after modification attempt: {obj.name}")
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Object Oriented Programming의 모든 레슨