-
State Pattern(상태 패턴)STUDY/디자인패턴 2025. 2. 16. 22:54
상태 패턴이란?
객체의 내부 상태가 변경될 때 해당 객체가 그의 행동을 변경할 수 있도록 하는 행동 디자인 패턴으로, 객체가 행동을 변경할 때 객체가 클래스를 변경한 것처럼 보일 수 있다.
상태 패턴은 객체의 모든 가능한 상태들에 대해 새 클래스들을 만들고 모든 상태별 행동들을 이러한 클래스들로 추출한다. 콘텍스트라는 원래 객체는 모든 행동을 자체적으로 구현하는 대신 현재 상태를 나타내는 상태 객체 중 하나에 대한 참조를 저장하고 모든 상태와 관련된 작업을 그 객체에 위임한다.
구조

- Context
구상 상태 객체 중 하나에 대한 참조를 저장하고 모든 상태별 작업을 구상 상태 객체에 위임한다. 콘텍스트는 상태 인터페이스를 통해 상태 객체와 통신하며, 새로운 상태 객체를 전달하기 위한 세터를 노출한다.
- State
상태별 메서드들을 선언한 인터페이스로, 이러한 메서드들은 모든 구상 상태에서 유효해야 한다.
- ConcreteStates
상태별 메서드들에 대한 자체적인 구현을 제공하는 구상 클래스로, 여러 상태에서 유사한 코드의 중복을 피하기 위하여 어떤 공통 행동을 캡슐화하는 중간 추상 클래스들을 제공할 수 있다. 상태 객체들은 콘텍스트 객체에 대한 역참조를 저장할 수 있고, 이 참조를 통해 상태는 콘텍스트 객체에서 모든 필요한 정보를 가져올 수 있고 상태 천이를 시작할 수 있다.
콘텍스트와 구상 상태들은 모두 콘텍스트의 다음 상태를 설정할 수 있으며, 콘텍스트에 연결된 상태 객체를 교체하여 실제 상태 천이를 수행할 수 있다.
예시

State
public abstract class State { Player player; State(Player player) { this.player = player; } public abstract String onLock(); public abstract String onPlay(); public abstract String onNext(); public abstract String onPrevious(); }ConcreteStates
public class LockedState extends State { LockedState(Player player) { super(player); player.setPlaying(false); } @Override public String onLock() { if (player.isPlaying()) { player.changeState(new ReadyState(player)); return "Stop playing"; } else { return "Locked..."; } } @Override public String onPlay() { player.changeState(new ReadyState(player)); return "Ready"; } @Override public String onNext() { return "Locked..."; } @Override public String onPrevious() { return "Locked..."; } }public class ReadyState extends State { public ReadyState(Player player) { super(player); } @Override public String onLock() { player.changeState(new LockedState(player)); return "Locked..."; } @Override public String onPlay() { String action = player.startPlayback(); player.changeState(new PlayingState(player)); return action; } @Override public String onNext() { return "Locked..."; } @Override public String onPrevious() { return "Locked..."; } }public class PlayingState extends State { PlayingState(Player player) { super(player); } @Override public String onLock() { player.changeState(new LockedState(player)); player.setCurrentTrackAfterStop(); return "Stop playing"; } @Override public String onPlay() { player.changeState(new ReadyState(player)); return "Paused..."; } @Override public String onNext() { return player.nextTrack(); } @Override public String onPrevious() { return player.previousTrack(); } }Context
public class Player { private State state; private boolean playing = false; private List<String> playlist = new ArrayList<>(); private int currentTrack = 0; public Player() { this.state = new ReadyState(this); setPlaying(true); for (int i = 1; i <= 12; i++) { playlist.add("Track " + i); } } public void changeState(State state) { this.state = state; } public State getState() { return state; } public void setPlaying(boolean playing) { this.playing = playing; } public boolean isPlaying() { return playing; } public String startPlayback() { return "Playing " + playlist.get(currentTrack); } public String nextTrack() { currentTrack++; if (currentTrack > playlist.size() - 1) { currentTrack = 0; } return "Playing " + playlist.get(currentTrack); } public String previousTrack() { currentTrack--; if (currentTrack < 0) { currentTrack = playlist.size() - 1; } return "Playing " + playlist.get(currentTrack); } public void setCurrentTrackAfterStop() { this.currentTrack = 0; } }적용
- 현재 상태에 따라 다르게 행동하는 객체가 있는 경우, 상태들의 수가 많은 경우, 그리고 상태별 코드가 자주 변경되는 경우
- 클래스 필드들의 현재 값들에 따라 클래스가 행동하는 방식을 변경하는 거대한 조건문들로 오염된 클래스가 있는 경우
- 유사한 상태들에 중복 코드와 조건분-기반 상태 머신의 천이가 많은 경우
장단점
장점
- 단일 책임 원칙. 특정 상태들과 관련된 코드를 별도의 클래스로 분리할 수있다.
- 개방/폐쇄 원칙. 기존 상태 클래스들 또는 콘텍스트를 변경하지 않고 새로운 상태들을 추가할 수 있다.
- 거대한 상태 머신 조건문들을 제거하여 콘텍스트의 코드를 단순화할 수 있다.
단점
- 상태 머신에 몇 가지 상태만 있거나 머신이 거의 변경되지 않을 때 상태 패턴을 적용하는 것은 과할 수 있다.
'STUDY > 디자인패턴' 카테고리의 다른 글
Template Method Pattern(템플릿 메서드 패턴) (0) 2025.03.03 Strategy Pattern(전략 패턴) (0) 2025.03.03 Observer Pattern(옵저버 패턴) (0) 2025.02.16 Memento Pattern(메멘토 패턴) (0) 2025.02.16 Mediator Pattern(중재자 패턴) (0) 2025.02.02