소프트웨어 개발자/디자인패턴

[디자인패턴] 행동 패턴 - 상태(State) 패턴

yubi5050 2024. 5. 4. 17:09

행동(Behavioal) 패턴

객체들이나 클래스들 사이에 상호 작용이나 책임(역할)분배를 하는 방법에 대한 패턴. 여러 객체에 책임을 분배하고, 객체 간의 종속도 최소화 한다. 

 

주요 패턴으론 다음 사항들이 있다.

  • 옵저버(Observer) 
  • 커맨드(Command)
  • 상태(State) <- 이번 글에서 다룰 내용
  • 전략(Strategy)
  • 책임 연쇄(Chain_Of_Responsibility)
  • 반복자(Iterator)
  • 중재자(Mediator)
  • 반복자(Visitor)

 

상태 (State) 패턴 이란

State 패턴은 행동 패턴 중 하나로 내부 상태가 변경될 때 객체의 행동을 교체한다. 객체가 상태(값)을 변경하여, 행동의 변화를 유발함으로서 클래스 자체가 변경된 것 처럼도 보일 수 있다. 

 

등장 배경

[초안, 검토, 출판본]의 상태를 가진 문서 객체가 있다고 가정할 때, 발행(publish)라는 메서드는 각 상태(State) 마다 다르게 작동할 것이다. 

  • 초안: 검토 상태로 변경 
  • 검토: 공개 상태로 변경 / 관리자만 가능
  • 출판본: 이미 발행되었기에, 아무것도 하지 않음

 

https://refactoring.guru/images/patterns/diagrams/state/problem1-2x.png

 

이 처럼 유한한 범위의 상태를 가지고 있는 프로그램에 상태 변화에 따라 다르게 행동할 수 있도록 설계 되는 것

이런 변경(transition)이라고 불리는 원칙은 유한한 범위이고, 미리 결정 되어야 한다.

 

 

예시 구조

State (상태)

  • 인터페이스로 기본적으로 해당 상태일 때 수행하는 작업 doThis(), doThat() 함수 등이 설계 되어 있다.
  • 객체 ConcreteState 가 존재하며, 해당 객체의 doThis() 함수는 (유한한) 상태 별 (미리 결정 된) 비즈니스 로직을 구현해둔다.

 

Context 

  • state 상태값을 참조한다. (객체 ConcreteState - 상태별 기능 구현 완)
  • 상태를 변경 가능한 changeState() 함수
  • Context의 doThis() 함수를 호출시 참조하는 상태(State)의 doThis() 함수를 호출 한다. 
  • doThis() 는 위에서 설명한 publish() 함수 역할과 동일

 

Client

  • 상태(State) 객체를 Context에 연결하고, 해당 상태에 맞는 행동(doThis()) 함수를 호출 해 둔다.
  • 이후 changeState()에 의해 상태가 바뀌는 경우 다시 doThis()를 호출하여 행위를 수행하게 한다.

 

https://refactoring.guru/images/patterns/diagrams/state/structure-en-indexed-2x.png

 

적용하면 좋은 경우

  • 상태(State)에 따라 다르게 동작할 때 / 상태의 경우의 수가 많을 때 / 상태 수정이 빈번할 때 사용이 권장 된다.
  • 클래스의 특정 필드에 따라, 클래스가 어떻게 동작할지 분기하는 거대한 조건문들로 클래스가 더러워졌을 때 권장한다. (SRP)
  • 상태 패턴은 조건문 분기들을 추출해내서 해당 상태 클래스의 메서드들로 옮길 수 있다 => 메인 클래스에서 상태 관련 임시 필드들과 헬퍼 메서드들을 분리할 수 있다.
  • 새로운 상태를 추가하거나 수정시, 상태 관련 로직이 State 클래스에 잘 분리되어 있어 유지 보수 비용이 낮다. (OCP에 유리)
  • 상태를 관리하는 비슷한 중복코드가 많은 경우 일 때 분리를 권장한다.

 

장단점

장점

  • SRP : 여러 클래스에 각각의 상태 코드들을 분리할 수 있다.
  • OCP : 기존 코드 변경 없이 새 상태를 추가할 수 있다.
  • 거대한 상태 머신 관련 조건문 들을 제거하여 메인 클래스(Context)를 보다 단순화할 수 있다.

 

단점

  • 상태 갯수가 적을 때는 오버엔지니어링 일 수 있다.

 

다른 패턴과의 관계

with. 브릿지(Bridge), 상태(State), 전략(Strategy), 어댑터(Adapter)

  • 다른 객체에게 할 일을 위임한다는 점에서 공통점을 가진다. 

 

 

vs 전략 (Strategy)

  • 상태 패턴은 전략 패턴의 확장으로 여겨질 수 있다. (유사하다)
  • 두 전략 모두 구성(Compostion)에 기반하여, 원하는 객체(컨텍스트)의 행동을 변경한다.
  • 전략(Strategy)은 이 객체들이 모두 독립적이고, 서로를 모른다.
  • 상태(State)는 상태 구현체들 간의 독립성을 강제하지 않고, 의지에 따라 컨텍스트의 상태도 변경 할 수 있다.