소프트웨어에 대한 모든 것

[디자인패턴][State] 상태 패턴 본문

시스템 설계 및 디자인/디자인 패턴

[디자인패턴][State] 상태 패턴

앤테바 2022. 3. 25. 16:05
반응형

상태 패턴 정의

  • 상태 패턴은 개체의 모든 가능한 상태에 대해 새 클래스를 만들고 모든 상태별 동작을 이러한 클래스로 추출하는 것
  • 각 상태의 모든 행동을 한 클래스에 집어 넣어서 행동을 국지화 시킴
스테이트 패턴을 이용하면
객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있습니다.
마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있습니다.
- by Head First Design Patterns

 

상태 패턴 장점

  • 각 상태의 행동을 별개의 클래스로 국지화해서 코드 수정이 용이
  • State에 따라서 분기하는 if 선언문 제거
  • 각 상태 변경에 대해서 닫혀 있도록 하고 새로운 상태 클래스 추가하는 확장에 대해 열려 있음

상태 패턴 클래스 다이어그램

State 패턴 Class Diagram

상태 패턴 활용

AudioPlayer를 예시로 사용합니다. 상태는 Locked 상태, Ready 상태, Playing 상태가 있습니다. State는 AudioPlay를 백 레퍼런스 하기 위해서 player 레퍼런스를 가지고 있습니다.

 

 

AudioPlay (Context) 클래스

from abc import ABC, abstractmethod


class AudioPlayer:
    def __init__(self):
        self.state = ReadyState(self)
        self.playing = False

    def change_state(self, state):
        self.state = state

    def next_song(self):
        print('play next song')

    def prev_song(self):
        print('play prev song')

    def start_plackback(self):
        print('play song')
        self.playing = True

    def stop_playback(self):
        print('stop playing')
        self.playing = False

    def click_locked(self):
        self.state.click_lock()

    def click_play(self):
        self.state.click_play()

    def click_next(self):
        self.state.click_next()

    def click_prev(self):
        self.state.click_prev()

상태 클래스

class State(ABC):
    def __init__(self, player):
        self.player = player

    @abstractmethod
    def click_lock(self): pass

    @abstractmethod
    def click_play(self): pass

    @abstractmethod
    def click_next(self): pass

    @abstractmethod
    def click_prev(self): pass

class LockedState(State):
    def __init__(self, player):
        super().__init__(player)

    def click_lock(self):
        if self.player.playing:
            self.player.change_state(PlayingState(self.player))
        else:
            self.player.change_state(ReadyState(self.player))

    def click_play(self):
        print('locked, do nothing')

    def click_next(self):
        print('locked, do nothing')

    def click_prev(self):
        print('locked, do nothing')


class ReadyState(State):
    def __init__(self, player):
        super().__init__(player)

    def click_lock(self):
        self.player.change_state(LockedState(self.player))

    def click_play(self):
        self.player.start_plackback()
        self.player.change_state(PlayingState(self.player))

    def click_next(self):
        self.player.next_song()

    def click_prev(self):
        self.player_prev_song()


class PlayingState(State):
    def __init__(self, player):
        super().__init__(player)

    def click_lock(self):
        self.player.change_state(LockedState(self.player))

    def click_play(self):
        self.player.stop_playback()
        self.player.change_state(ReadyState(self.player))

    def click_next(self):
        self.player.next_song()

    def click_prev(self):
        self.player.prev_song()

Client 코드

audio = AudioPlayer()
audio.click_play()
audio.click_next()
audio.click_prev()
audio.click_locked()
audio.click_next()
audio.click_locked()
audio.click_play()

출력

play song
play next song
play prev song
locked, do nothing
stop playing

스트래티지 패턴과 차이

  • 스테이트 패턴을 사용할 때는 상태 객체에 일련의 행동이 캡슐화됩니다.
  • 스트래티지 패턴을 사용할 때는 일반적으로 클라이언트에서 컨텍스트 객체한테 어떤 전략 객체를 사용할지를 지정해 줍니다.

 

 

함께 보면 좋은 글

2022.03.23 - [시스템 설계 및 디자인/객체지향 SOLID] - [디자인패턴][SOLID] 개방/폐쇄 원칙

 

참고 자료

https://refactoring.guru/design-patterns/state

https://brownbears.tistory.com/571

https://johngrib.github.io/wiki/pattern/state/

https://blog.devgenius.io/design-pattern-state-e0ed37cd853a

 

 

반응형
Comments