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

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


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(비공개): 허락된 학생만 등록 가능, 등록한 학생만 리뷰 가능

public interface State {
void addReview(String review, Student student);
void addStudent(Student student);
}
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 상태로 바꿈. 객체가 스스로 자기 상태 전환
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가 사라지고 메서드 호출이 오면 현재 상태 객체에 그대로 위임. 앞으로 새 상태를 추가하려면 클래스 하나 새로 만들면 됨
장점
단점