객체가 상태에 따라 행위를 다르게 할 때, 직접 상태를 체크하여 상태에 따른 행위를 호출하는 것이 아니라 상태를 객체화하여 필요에 따라 다르게 행동하도록 위임하는 디자인 패턴
객체의 특정 상태를 클래스로 선언하고, 클래스에서는 해당 상태에서 할 수 있는 행위들을 메서드로 정의
이러한 각 상태 클래스들을 인터페이스로 캡슐화 하여, 클라이언트에서 인터페이스를 호출하는 방식을 말한다.
여기서 상태란, 객체가 가질 수 있는 어떤 조건이나 상황을 의미한다.

동일한 동작을 객체 상태에 따라 다르게 처리해야 할 경우 사용한다.
예를 들어, 하나의 객체에 여러가지 상태(정지, 상승, 하강)가 존재할 때, 패턴을 사용하지 않고 프로그래밍을 사용한다면 조건문을 사용하여 처리해야 한다. 그리고 신규 상태가 추가된다면 코드를 수정해야한다.
이런 경우, 위 그림처럼 객체 상태를 캡슐화하여 클래스화(state interface) 함으로써 그것을 참조하게 하는 방식으로 상태에 따라 다르게 처리할 수 있다. 따라서 변경시 수정을 최소화할 수 있다.
public class NonElevator {
public static final String up = "UP";
public static final String down = "DOWN";
public static final String stop = "STOP";
private String currentState = "";
public NonElevator() {
this.currentState = stop;
}
public void setState(String state) {
this.currentState = state;
}
public void pushUpBtn() {
if (currentState.equals(up)) {
System.out.println("동작 없음");
} else {
System.out.println("올라감");
currentState = up;
}
}
public void pushDownBtn() {
if (currentState.equals(down)) {
System.out.println("동작 없음");
} else {
System.out.println("내려감");
currentState = down;
}
}
public void pushStopBtn() {
if (currentState.equals(stop)) {
System.out.println("동작 없음");
} else {
System.out.println("멈춤");
currentState = stop;
}
}
}
위 같은 상황에서 모든 상태에 대한 조건문들이 필요하다.

각 상태 (up, down stop)을 클래스로 정의한 후, 인터페이스로 묶는다.(캡슐화)
interface ElevatorState
public interface ElevatorState {
void pushUpButton();
void pushDownButton();
void pushStopButton();
}
class DownState
public class DownState implements ElevatorState {
private static DownState downState;
private DownState() {}
public static DownState getInstance() {
if (downState == null) {
downState = new DownState();
}
return downState;
}
@Override
public void pushUpButton() {
System.out.println("올라감");
}
@Override
public void pushDownButton() {
System.out.println("동작없음");
}
@Override
public void pushStopButton() {
System.out.println("멈춤");
}
}
class UpState
public class UpState implements ElevatorState {
private static UpState upState;
public static UpState getInstance() {
if (upState == null) {
upState = new UpState();
}
return upState;
}
@Override
public void pushUpButton() {
System.out.println("동작없음");
}
@Override
public void pushDownButton() {
System.out.println("내려감");
}
@Override
public void pushStopButton() {
System.out.println("멈춤");
}
}
class StopState
public class StopState implements ElevatorState{
private static StopState stopState;
private StopState() {}
public static StopState getInstance() {
if (stopState == null) {
stopState = new StopState();
}
return stopState;
}
@Override
public void pushUpButton() {
System.out.println("올라감");
}
@Override
public void pushDownButton() {
System.out.println("내려감");
}
@Override
public void pushStopButton() {
System.out.println("동작없음");
}
}
class AdaptElevator
public class AdaptElevator {
private ElevatorState elevatorState;
public AdaptElevator() {
this.elevatorState = StopState.getInstance();
}
public void setElevatorState(ElevatorState state) {
this.elevatorState = state;
}
public void pushUpButton() {
elevatorState.pushUpButton();
this.setElevatorState(UpState.getInstance());
}
public void pushDownButton() {
elevatorState.pushDownButton();
this.setElevatorState(DownState.getInstance());
}
public void pushStopButton() {
elevatorState.pushStopButton();
this.setElevatorState(StopState.getInstance());
}
}
Main
public class Main {
public static void main(String[] args) {
AdaptElevator elevator = new AdaptElevator();
elevator.pushStopButton();
elevator.pushDownButton();
elevator.pushStopButton();
elevator.pushUpButton();
elevator.pushStopButton();
elevator.pushStopButton();
elevator.pushStopButton();
}
}

장점 :
- 상태에 따른 동작을 개별 클래스로 옮겨서 관리할 수 있다.
- 상태와 관련된 모든 동작을 각각의 상태 클래스에 분산시킴으로써, 코드 복잡도를 줄일 수 있다.
- 단일 책임의 원칙 준수(SRP : 특정 상태와 관련된 코드를 별도의 클래스로 구성)
- 개방 폐쇄의 원칙 준수 (OCP : 기존 State 클래스나 컨텍스트를 변경하지 않고 새 State를 도입할 수 있다.)
- 하나의 상태 객체만 사용하여 상태 변경을 하므로 일관성 없는 상태 주입을 방지하는데 도움이 된다.
단점 :
- 상태별로 클래스를 생성하므로, 관리해야할 클래스 수 증가
- 상태 클래스 개수가 많고 상태 큐칙이 자주 변경된다면, Context 의 상태 변경 코드가 복잡해지게 될 수 있다.
- 객체에 적용할 상태가 몇가지 밖에 없거나 거의 상태 변경이 이루어지지 않는 경우 패턴을 적용하는 것이 과도할 수 있다.
공통점 :
- 인터페이스를 사용하여 구현 클래스를 캡슐화한다. 전략 패턴에서는 할인 정책의 종류가 될 수 있고, 상태 패턴에서는 위의 예시인 자판기의 여러 상태가 될 수 있습니다.
- 위 언리 덕분에, Context 클래스에서는 어떠한 하위 클래스를 할당받는지 알지 못한 상태로 단순히 Strategy나 State 객체의 추상 메서드를 실행한다. 즉, 두 패턴 모두 Context 클래스는 영향을 받지 않고 유연하게 변화에 대처할 수 있다.
차이점 :
전략 패턴은 클라이언트 객체가 Context 객체에게 다른 트정 객체를 지정해주고 실행하게 한다.
따라서, 프로그램 실행시 Context 객체가 실행할 객체를 외부(클라이언트 객체)에서 유연하게 지정할 수 있는 장점이 있다.
Context 객체 내에 참조 멤버 변수를 사용하여 하위 클래스에 댇체의 참조를 전달하면, Context객체의 코드 변경 없이도 Context의 행동을 변경할 수 있다. 즉, 다형성과 추상 메서드 호출을 이용하여 Context 클래스의 코드를 간략하게 할 수 있는 방법이다.
상태 패턴도 추상 메서드 호출을 이용한다는 점에서 전략 패턴과 유사하다. 하지만, 상태 패턴은 외부(클라이언트 객체)의 개입없이 상태 객체 내부에서 현재 상태에 따라 Context 객체의 멤버인 상태 객체를 변경하여 사용한다는 점에서 차이가 있다.
참조 :
https://victorydntmd.tistory.com/294
[디자인패턴] 스테이트 패턴 ( State Pattern )
스테이트 패턴 ( State Pattern )스테이트 패턴은 객체가 특정 상태에 따라 행위를 달리하는 상황에서,자신이 직접 상태를 체크하여 상태에 따라 행위를 호출하지 않고,상태를 객체화 하여 상태가
victorydntmd.tistory.com
https://tecoble.techcourse.co.kr/post/2021-04-26-state-pattern/
상태 패턴(State Pattern)을 사용해보자
🥰 😁 😐 😩 🤬 😴 상태(현재 진행중인 행위)를 나타내기 위해 어떤 방식을 사용해 왔는가? Enum 열거형(Enum) 이란 서로 연관된 상수들의 집합이다. 상태를 나타내기에는 정말 편리한 방식이라
tecoble.techcourse.co.kr
https://johngrib.github.io/wiki/pattern/state/
스테이트 패턴 (State Pattern)
객체의 내부 상태에 따라 행동을 변경할 수 있다.
johngrib.github.io
| [Design Pattern] Adapter Pattern (0) | 2023.03.28 |
|---|---|
| [Design Pattern] Composite Pattern (0) | 2023.03.28 |
| [Design Pattern] Builder Pattern (0) | 2023.03.25 |
| [Design Pattern] Decorator Pattern (0) | 2023.03.24 |
| [Design Pattern] Strategy Pattern (0) | 2023.03.23 |