Design Pattern_Behavioral Patterns_State

박지홍·2026년 5월 4일

DesignPattern

목록 보기
12/12

상태 패턴

객체 내부 상태에 따라 객체의 행동이 달라지는 패턴

예제) 온라인 강의 플랫폼에서 강의의 상태 draft, published, private에 따라 처리가 달라짐

before

OnlineCourse.java — enum으로 상태를 표현하고, 메서드마다 if-else로 분기 처리

public class OnlineCourse {

    public enum State {
        DRAFT, PUBLISHED, PRIVATE
    }

    private State state = State.DRAFT;
    private List<String> reviews = new ArrayList<>();
    private List<Student> students = new ArrayList<>();

    public void addReview(String review, Student student) {
        if (this.state == State.PUBLISHED) {
            this.reviews.add(review);
        } else if (this.state == State.PRIVATE && this.students.contains(student)) {
            this.reviews.add(review);
        } else {
            throw new UnsupportedOperationException("리뷰를 작성할 수 없습니다.");
        }
    }

    public void addStudent(Student student) {
        if (this.state == State.DRAFT || this.state == State.PUBLISHED) {
            this.students.add(student);
        } else if (this.state == State.PRIVATE && availableTo(student)) {
            this.students.add(student);
        } else {
            throw new UnsupportedOperationException("학생을 해당 수업에 추가할 수 없습니다.");
        }

        if (this.students.size() > 1) {
            this.state = State.PRIVATE;  // 상태 자동 전환
        }
    }
}

DRAFT(초안): 아직 공개 전, 누구나 추가 가능하지만 리뷰는 못 씀
PUBLISHED(공개): 누구나 등록 가능, 누구나 리뷰 가능
PRIVATE(비공개): 허락된 학생만 등록 가능, 등록한 학생만 리뷰 가능

문제점

  1. 상태 분기가 메서드마다 흩어져 있어서 특정 상태에서 동작을 알려면 관련 메서드를 다 봐야함. 한 상태의 행동이 한 군데 모여있지 않고 곳곳에 분산돼 있음
  2. 새 상태 추가가 힘듬. 만약 폐강 상태를 추가하면 if-else가지가 있는 모든 메서드를 수정해야함. OCP 위반

after - 각 상태를 클래스로

State.java — 새로 등장한 인터페이스

public interface State {
    void addReview(String review, Student student);
    void addStudent(Student student);
}

Draft.java — 초안 상태 클래스

public class Draft implements State {

    private OnlineCourse onlineCourse;  // ← Context 참조 보관

    public Draft(OnlineCourse onlineCourse) {
        this.onlineCourse = onlineCourse;
    }

    @Override
    public void addReview(String review, Student student) {
        throw new UnsupportedOperationException("드래프트 상태에서는 리뷰를 남길 수 없습니다.");
    }

    @Override
    public void addStudent(Student student) {
        this.onlineCourse.getStudents().add(student);
        if (this.onlineCourse.getStudents().size() > 1) {
            this.onlineCourse.changeState(new Private(this.onlineCourse));
            // ↑ 상태 전환! 이 시점에 자기 자신을 Private으로 바꿔버림
        }
    }
}

학생이 2명 이상 되면 changeState(new Private(this.onlineCourse))로 자기 자신을 Private 상태로 바꿈. 객체가 스스로 자기 상태 전환

OnlineCourse.java — Context의 변신

public class OnlineCourse {

    private State state = new Draft(this);  // ← 처음엔 Draft 상태로 시작

    private List<Student> students = new ArrayList<>();
    private List<String> reviews = new ArrayList<>();

    public void addStudent(Student student) {
        this.state.addStudent(student);  // ← 그냥 위임!
    }

    public void addReview(String review, Student student) {
        this.state.addReview(review, student);  // ← 그냥 위임!
    }

    public void changeState(State state) {
        this.state = state;
    }

    // getter들...
}

엄청 깔끔해짐. if-else가 사라지고 메서드 호출이 오면 현재 상태 객체에 그대로 위임. 앞으로 새 상태를 추가하려면 클래스 하나 새로 만들면 됨

장단점

  • 장점

    • 상태에 따른 동작을 개별 클래스에 옮겨서 관리 가능
    • 기존의 특정 상태에 따른 동작을 변경하지 않고 새로운 상태에 다른 동작 추가 가능
    • 코드 복잡도 줄일 수 있음
  • 단점

    • 복잡도 증가

실제 예제

  • Spring State Machine
    스프링엔 상태 머신을 위한 별도 프로젝트(spring-statemachine)가 있음. 주문 처리 같이 상태가 복잡한 도메인에서 많이 씀. 이걸 State 패턴 기반으로 깔끔하게 관리
  • 게임 캐릭터의 상태

0개의 댓글