상태 패턴은 객체가 내부적으로 상태를 변경할 때마다 객체의 행동을 바꾸는 패턴이다.
이 패턴은 객체의 상태에 따라 객체가 수행하는 동작을 동적으로 변경할 수 있게 한다.
상태 패턴을 사용해야 하는 상황
객체의 행동이 상태에 따라 변할 때 | 객체의 행동이 특정 상태에 의존적이고, 상태가 변경될 때마다 객체의 행동이 동적으로 변해야 할 때 유용하다. |
복잡한 조건문 대체 | 상태에 따른 각각의 동작을 개별적인 상태 클래스로 캡슐화하면 복잡한 조건문을 피할 수 있다. |
유집보수 용이성 | 새로운 상태가 추가되거나 기존 상태의 동작이 변경될 때, 해당 상태에 대한 클래스만 수정하면 되므로 유지보수가 용이하다. |
상태 패턴을 사용하는 경우의 문제점
클래스 수 증가 | 각 상태를 별도의 클래스로 구현하기 때문에 클래스의 수가 증가한다 |
상태 전환 오버헤드 | 상태가 변경될 때마다 새로운 상태 객체를 생성해야 하므로 객체 생성 및 소멸에 따른 부하가 발생한다. |
순환 참조 | 상태 객체 간에 순환 참조가 발생할 수 있으며 메모리 누수로 이어질 수 있다 |
상태 전환 로직의 중앙 집중화 | 상태 전환 로직이 클라이언트나 컨텍스트 클래스에 직접 구현되는 경우가 있을 수 있다. 이로 인해 상태 패턴의 장점이 퇴색되고, 전환 로직의 중앙 집중화로 복잡성이 높아질 수 있다. |
// GumballMachine의 상태 인터페이스. 각 상태는 이 인터페이스를 구현하여 구체적인 동작을 정의한다.
public interface State {
void insertQuarter(); // 동전을 삽입하는 동작
void ejectQuarter(); // 동전을 반환하는 동작
void turnCrank(); // 손잡이를 돌리는 동작
void dispense(); // 껌볼을 내보내는 동작
}
// GumballMachine 클래스는 껌볼 머신의 다양한 상태를 관리한다.
public class GumballMachine {
// 상태와 해당 State 인스턴스를 매핑하는 맵
private final Map<StateEnum, State> stateMap = Map.of(
StateEnum.SOLD_OUT, new SoldOutState(this),
StateEnum.NO_QUARTER, new NoQuarterState(this),
StateEnum.HAS_QUARTER, new HasQuarterState(this),
StateEnum.SOLD, new SoldState(this)
);
private State state; // 현재 상태
private int count; // 껌볼 개수
// 생성자: 껌볼 개수를 받아 초기 상태를 설정
public GumballMachine(int numberGumballs) {
this.count = numberGumballs;
if (numberGumballs > 0) {
state = stateMap.get(StateEnum.NO_QUARTER); // 동전 없음 상태로 초기화
} else {
state = stateMap.get(StateEnum.SOLD_OUT); // 품절 상태로 초기화
}
}
// 동전을 삽입하는 메서드
public void insertQuarter() {
state.insertQuarter();
}
// 동전을 반환하는 메서드
public void ejectQuarter() {
state.ejectQuarter();
}
// 손잡이를 돌리는 메서드
public void turnCrank() {
state.turnCrank();
state.dispense(); // 손잡이를 돌린 후 알맹이를 내보낸다.
}
// 알맹이를 내보내는 메서드
public void releaseBall() {
System.out.println("알맹이를 내보내고 있습니다.");
if (count > 0) {
count = count - 1;
}
}
// 현재 상태를 변경하는 메서드
public void changeState(StateEnum stateEnum) {
this.state = stateMap.get(stateEnum);
}
// 껌볼 개수를 반환하는 메서드
public int getCount() {
return this.count;
}
}
// GumballMachine의 상태를 나타내는 열거형. 각 상태는 별도의 클래스로 구현된다.
public enum StateEnum {
SOLD, // 판매 중
SOLD_OUT, // 품절
NO_QUARTER, // 동전 없음
HAS_QUARTER // 동전 있음
}
// GumballMachine의 '판매 중' 상태를 나타내는 클래스.
public class SoldState implements State {
private final GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("알맹이를 내보내고 있습니다.");
}
@Override
public void ejectQuarter() {
System.out.println("이미 알맹이를 뽑았습니다.");
}
@Override
public void turnCrank() {
System.out.println("알맹이를 내보내고 있습니다.");
}
@Override
public void dispense() {
gumballMachine.releaseBall(); // 알맹이를 내보낸다
if (gumballMachine.getCount() > 0) {
gumballMachine.changeState(StateEnum.NO_QUARTER); // 동전 없음 상태로 변경
} else {
System.out.println("알맹이가 없습니다.");
gumballMachine.changeState(StateEnum.SOLD_OUT); // 품절 상태로 변경
}
}
}
// GumballMachine의 '품절' 상태를 나타내는 클래스.
public class SoldOutState implements State {
private final GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("알맹이가 없습니다."); // 품절 상태에서는 동전을 삽입할 수 없습니다.
}
@Override
public void ejectQuarter() {
System.out.println("알맹이가 없습니다."); // 반환할 동전이 없습니다.
}
@Override
public void turnCrank() {
System.out.println("알맹이가 없습니다."); // 손잡이를 돌려도 알맹이가 나오지 않습니다.
}
@Override
public void dispense() {
System.out.println("알맹이가 없습니다."); // 내보낼 알맹이가 없습니다.
}
}
// GumballMachine의 '동전 없음' 상태를 나타내는 클래스.
public class NoQuarterState implements State {
private final GumballMachine gumballMachine;
NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("동전을 넣으셨습니다.");
gumballMachine.changeState(StateEnum.HAS_QUARTER); // 동전 있음 상태로 변경
}
@Override
public void ejectQuarter() {
System.out.println("동전을 넣어 주세요.");
}
@Override
public void turnCrank() {
System.out.println("동전을 넣어 주세요.");
}
@Override
public void dispense() {
System.out.println("동전을 넣어 주세요.");
}
}
// GumballMachine의 '동전 있음' 상태를 나타내는 클래스.
public class HasQuarterState implements State {
private final GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("동전은 한 개만 넣어 주세요.");
}
@Override
public void ejectQuarter() {
System.out.println("동전이 반환됩니다.");
gumballMachine.changeState(StateEnum.NO_QUARTER); // 동전 없음 상태로 변경
}
@Override
public void turnCrank() {
System.out.println("손잡이를 돌리셨습니다.");
gumballMachine.changeState(StateEnum.SOLD); // 판매 중 상태로 변경
}
@Override
public void dispense() {
System.out.println("알맹이를 내보낼 수 없습니다.");
}
}
728x90
'디자인 패턴' 카테고리의 다른 글
template method pattern (0) | 2024.07.05 |
---|---|
Singletone Pattern (0) | 2024.06.15 |
Proxy Pattern (0) | 2024.06.15 |
Observer Pattern (0) | 2024.05.30 |
Mediator Pattern (0) | 2024.05.28 |