Exist Technote
[Python] State Pattern (상태 패턴) 본문
State Pattern이란?
- 객체 내부의 상태에 따라 스스로 행동을 변경할 수 있게끔 허가하는 패턴.
- 마치 자신의 클래스를 바꾸는 것처럼 보이게 된다.
예제
- 변경 전의 라디오 클래스
class Radio:
STATIONS = {
"AM": ["1250", "1380", "1510"],
"FM": ["81.3", "89.1", "103.9"]
}
def __init__(self):
self.modulation = "AM"
self.stations_point = 0
def toggle(self):
if self.modulation == "AM":
self.modulation = "FM"
print("FM으로 전환")
else:
self.modulation == "AM"
print("AM으로 전환")
def scan(self):
self.stations_point += 1
if self.stations_point >= len(self.STATIONS[self.modulation]):
self.stations_point = 0
station = self.STATIONS[self.modulation][self.stations_point]
print(f"[{self.modulation}] 스캔중... {station}")
if __name__ == "__main__":
radio = Radio()
actions = [radio.scan] * 2 + [radio.toggle] + [radio.scan] * 2
actions *= 2
for action in actions:
action()
# 1. toggle, scan 등의 메소드 동작 과정이 분명하고 명확하게 드러나지 않는다.
# 2. 만일 새로운 modulation 주파수 변조 방식이 등록될 경우 기존 코드를 반드시 변경해야 한다.
# 3. STATIONS의 길이가 다를 경우 scan의 조건문을 수정해야 한다.
# 주파수 변조 방식의 상태에 따라 사용되는 객체를 변경하며 사용할 것이다.
# 어떤 주파수 변조 방식에도 공통적으로 사용되는 부분을 슈퍼 클래스에 정의한다.
class Modulation:
def scan(self):
self.pos += 1
if self.pos == len(self.stations):
self.pos = 0
print("[{}] 스캔중... {}".format(self.name, self.stations[self.pos]))
# 달리 동작하는 부분은 서브클래스에서 정의한다.
class AmModulation(Modulation):
def __init__(self, radio):
self.radio = radio
self.stations = ["1250", "1380", "1510"]
self.pos = 0
self.name = "AM"
def toggle(self):
print("FM으로 전환")
self.radio.state = self.radio.fm_modulation
class FmModulation(Modulation):
def __init__(self, radio):
self.radio = radio
self.stations = ["81.3", "89.1", "103.9"]
self.pos = 0
self.name = "FM"
def toggle(self):
print("AM으로 전환")
self.radio.state = self.radio.am_modulation
class Radio:
def __init__(self):
# 각 주파수 변조 방식(상태) has방식으로 보유하도록 설계.
self.am_modulation = AmModulation(self)
self.fm_modulation = FmModulation(self)
self.modulation = self.am_modulation
# self.modulation에 am_modulation이나 fm_modulation 둘중 무엇이 오더라도
# 공통적으로 사용될 수 있는 두 메서드.
def toggle(self):
self.modulation.toggle()
def scan(self):
self.modulation.scan()
if __name__ == "__main__":
radio = Radio()
actions = [radio.scan] * 2 + [radio.toggle] + [radio.scan] * 2
actions *= 2
for action in actions:
action()
# 라디오와 주파수 상태가 분리되어 새로운 modulation이 등록되더라도 쉽게 관리할 수 있다.
# 로직의 흐름을 분명하게 파악할 수 있다.
# 불필요한 조건문을 나열하지 않아도 된다.
# 새로 기능을 추가할 때, 그것이 라디오의 범위인지 변조 방식의 범위인지 명확하게 구분할 수 있다.
여담
- 엘레베이터의 여러 조건(내려가는 중, 버튼이 어디서 눌렸다 등등)의 상황에서 동작을 변경해야 할 경우 사용할 수 있겠다.
- "유한상태기계"와 관련이 깊다. 자세한 정보는 이곳에서 확인할 수 있다.
- 가질수 있는 상태가 한정되며, 한번에 하나의 상태만 될 수 있고, 각 상태는 다른 이벤트를 지니며, 다음 상태로 변경되는 전이 조건을 지녀야 한다는 의미이다.
- "객체 내부의 상태에 따라 스스로 행동을 변경할 수 있게끔 허가하는 패턴"의 의미를 다음과 같이 풀어낼 수 있을 듯 하다.
- 알고리즘이 유한상태기계(쉽게 말해, 순환 순서도)로 정리될 수 있을 때 각 상태를 객체화(동일한 인터페이스, 상태에 따른 동작, 다음번 상태로 전이할 수 있는 메소드 정의)하여 사용하는 패턴.
'Design Pattern > Behavioral' 카테고리의 다른 글
[Python] Template Method Pattern (템플릿 메소드) (0) | 2020.10.18 |
---|---|
[Python] Registry Pattern (레지스트리 패턴) (0) | 2020.10.15 |
[Python] Chaining Method Pattern (체이닝 메소드 패턴) (0) | 2020.10.14 |
[Python] Observer Pattern (옵저버 패턴) (0) | 2020.10.10 |
Comments