Menu
Coddy logo textTech

상태 패턴

Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 87개 중 74번째.

상태 패턴(State Pattern)은 객체의 내부 상태가 변경될 때 객체가 동작을 변경할 수 있도록 하는 행동 디자인 패턴입니다. 객체는 마치 클래스를 바꾸는 것처럼 보이지만, 실제로는 동작을 서로 다른 상태 객체에 위임합니다. 자판기를 생각해 보세요. 버튼을 눌렀을 때의 반응은 자판기가 대기 중인지, 동전이 투입되었는지, 아니면 제품을 내보내는 중인지에 따라 달라집니다.

다양한 상태를 처리하기 위해 복잡한 조건문을 사용하는 대신, 각 상태를 자체 클래스로 캡슐화합니다. 이 패턴은 현재 상태에 대한 참조를 유지하는 Context와 각 상태에 대한 동작을 정의하는 State 클래스로 구성됩니다:

interface State {
    void handle(Context context);
}

class Context {
    private State state;
    
    public void setState(State state) {
        this.state = state;
    }
    
    public void request() {
        state.handle(this);
    }
}

각 구체적인 상태는 동작을 구현하며 다른 상태로의 전이를 트리거할 수 있습니다:

class IdleState implements State {
    public void handle(Context context) {
        System.out.println("Idle: Insert coin to start");
        context.setState(new ActiveState());
    }
}

class ActiveState implements State {
    public void handle(Context context) {
        System.out.println("Active: Processing...");
        context.setState(new IdleState());
    }
}

컨텍스트는 요청을 현재 상태로 위임하며, 상태가 변경됨에 따라 동작이 자동으로 변경됩니다:

Context context = new Context();
context.setState(new IdleState());

context.request();  // 대기 상태: 시작하려면 동전을 넣으세요
context.request();  // 활성 상태: 처리 중...

상태 패턴은 객체의 행동이 상태에 크게 의존하고 런타임에 변경되어야 할 때 이상적입니다. 이 패턴은 거대한 조건문 블록을 제거하고 새로운 상태를 추가하는 것을 간단하게 만듭니다. State 인터페이스를 구현하는 새로운 클래스를 생성하기만 하면 됩니다.

challenge icon

챌린지

쉬움

상태 패턴(State Pattern)을 사용하여 문서 워크플로 시스템을 구축해 봅시다! 문서가 초안(Draft), 검토(Review), 게시(Published)와 같은 여러 단계를 거치는 방식을 모델링하는 시스템을 만들게 됩니다. 각 상태는 어떤 동작이 가능한지, 그리고 문서를 편집하거나 승인하려고 할 때 문서가 어떻게 동작하는지를 결정합니다.

코드는 네 개의 파일로 구성됩니다:

  • DocumentState.java: 모든 상태가 구현할 DocumentState 인터페이스를 정의합니다. 이 인터페이스는 두 가지 메서드를 선언해야 합니다: 문서 편집을 시도하는 edit(Document doc)와 문서를 승인하거나 다음 단계로 진행시키려는 approve(Document doc)입니다.
  • States.java: DocumentState를 구현하는 세 개의 구체적인 상태 클래스를 생성합니다:

    DraftState - edit이 호출되면 Editing draft...를 출력합니다. approve가 호출되면 Draft approved. Moving to review.를 출력하고 문서를 ReviewState로 전환합니다.

    ReviewState - edit이 호출되면 Cannot edit during review.를 출력합니다(상태 변경 없음). approve가 호출되면 Review complete. Publishing document.를 출력하고 PublishedState로 전환합니다.

    PublishedState - edit이 호출되면 Cannot edit published document.를 출력합니다. approve가 호출되면 Document already published.를 출력합니다. 두 동작 모두 상태를 변경하지 않습니다.

  • Document.java: 현재 상태를 유지하는 컨텍스트(context) 클래스를 생성합니다. Document 클래스는 다음을 포함해야 합니다:

    현재 DocumentState를 보유하는 private 필드이며, 생성자에서 DraftState로 초기화됩니다.

    현재 상태를 변경하는 setState(DocumentState state) 메서드.

    현재 상태의 edit 메서드에 작업을 위임하는 edit() 메서드.

    현재 상태의 approve 메서드에 작업을 위임하는 approve() 메서드.

    현재 상태의 단순 클래스 이름을 반환하는 getStatus() 메서드(getClass().getSimpleName()을 사용하세요).

  • Main.java: 상태 시스템을 시연합니다! 하나의 입력을 받습니다: 각 동작이 edit 또는 approve인 쉼표로 구분된 문자열 시퀀스입니다 (예: edit,approve,edit,approve).

    Document를 생성하고 Status: [state] 형식으로 초기 상태를 출력합니다. 그런 다음 각 동작을 순서대로 처리하며 문서의 적절한 메서드를 호출합니다. 각 동작 후에 현재 상태를 출력합니다.

하나의 입력을 받습니다: 쉼표로 구분된 동작 문자열입니다.

예를 들어, 입력이 edit,approve,approve인 경우 출력은 다음과 같습니다:

Status: DraftState
Editing draft...
Status: DraftState
Draft approved. Moving to review.
Status: ReviewState
Review complete. Publishing document.
Status: PublishedState

동일한 approve() 호출이 문서의 현재 상태에 따라 어떻게 다른 결과를 생성하는지 주목하세요. 이것이 상태 패턴의 핵심입니다! 문서는 자신의 상태를 확인하기 위해 조건문을 사용하지 않습니다. 대신, 현재 상태 객체에 동작을 위임하며, 상태 객체는 동작을 적절히 처리하고 필요한 경우 전환을 트리거합니다.

치트 시트

상태 패턴(State Pattern)은 객체의 내부 상태가 변경될 때 객체가 동작을 변경할 수 있도록 허용하는 행동 디자인 패턴입니다. 이는 복잡한 조건문을 사용하는 대신 동작을 서로 다른 상태 객체에 위임함으로써 이루어집니다.

이 패턴은 현재 상태에 대한 참조를 유지하는 Context와 각 상태에 대한 동작을 정의하는 State 클래스들로 구성됩니다.

interface State {
    void handle(Context context);
}

class Context {
    private State state;
    
    public void setState(State state) {
        this.state = state;
    }
    
    public void request() {
        state.handle(this);
    }
}

각 구체적인 상태(concrete state)는 동작을 구현하며 다른 상태로의 전이를 트리거할 수 있습니다.

class IdleState implements State {
    public void handle(Context context) {
        System.out.println("Idle: Insert coin to start");
        context.setState(new ActiveState());
    }
}

class ActiveState implements State {
    public void handle(Context context) {
        System.out.println("Active: Processing...");
        context.setState(new IdleState());
    }
}

컨텍스트는 요청을 현재 상태로 위임하며, 상태가 변경됨에 따라 동작이 자동으로 바뀝니다.

Context context = new Context();
context.setState(new IdleState());

context.request();  // 유휴 상태: 시작하려면 동전을 넣으세요
context.request();  // 활성 상태: 처리 중...

상태 패턴은 객체의 동작이 상태에 크게 의존하고 런타임에 변경되어야 할 때 이상적입니다. 이는 거대한 조건문 블록을 제거하고 새로운 상태를 추가하는 것을 간단하게 만듭니다.

직접 해보기

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: 새로운 Document 생성
        
        // TODO: 초기 상태를 "Status: [state]" 형식으로 출력
        
        // TODO: 입력을 쉼표로 분리하여 개별 액션들을 가져오기
        
        // TODO: 각 액션을 순회하며 반복
        // - 액션이 "edit"이면, document의 edit() 호출
        // - 액션이 "approve"이면, document의 approve() 호출
        // - 각 액션 이후에, 현재 상태를 출력
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨