티스토리 뷰
1. 스테이트 패턴이란?
스테이트 패턴을 이용하면 객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있다.
마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있다.
구성요소

Context
- 클라이언트가 필요한 인터페이스르 정의한다.
- 현재 상태를 나타내는 ConcreteState 서브클래스를 유지한다.
State
- 특정 상태에 해당되는 기능들이 정의된 인터페이스를 선언하고 있다.
ConcreteState
- 각 서브클래스는 해당되는 상태의 기능들을 구현한다.
2. 동기
장점
- 상태에 특화된 행동들을 분리하고, 새로운 행동을 추가하더라도 다른 행동에 영향을 주지 않는다.
- 코드 복잡도를 줄일 수 있다.
- 상태의 전환이 명시적으로 변한다.
단점
- 구조의 복잡도가 증가한다.
3. 구현
구현 시 고려사항
1) 누가 상태 변경을 담당하는가?
- 상태 전환을 중앙화하는 방법이 있고 탈중앙화하는 방법이 있다.
- 만약 상태가 바뀌는 방식이 이미 정해져 있다면, Context 내에서 상태 변경 코드를 모두 구현할 수 있다.
- 그렇지 않고 각 State마다 다음 State에 대한 전환을 명시할 수 있지만, 이는 각 상태끼리 커플링 된다는 단점이 있다.
2) 테이블로 상태 변경 담당하기
- 장점: 한 테이블에서 모든 상태에 대한 명시가 되어있기 때문에 관리가 용의하고, 코드 변경을 하지 않아도 된다.
- 단점: lookup에 소요되는 시간이 있고, 상태 변경이 어떻게 되는건지 테이블을 통해 파악하기 어렵다.
3) 상태 객체의 생성과 파괴
- 필요할 때만 생성하고 파괴할 것인가
- 미리 생성해두고 삭제하는 과정은 생략할 서인가
4) 동적 상속 사용
구현예시
public class Client {
public static void main(String[] args) {
Student student = new Student("whiteship");
OnlineCourse onlineCourse = new OnlineCourse();
Student keesun = new Student("keesun");
keesun.addPrivateCourse(onlineCourse);
onlineCourse.addStudent(student);
onlineCourse.changeState(OnlineCourse.State.PRIVATE);
onlineCourse.addStudent(keesun);
onlineCourse.addReview("hello", student);
System.out.println(onlineCourse.getState());
System.out.println(onlineCourse.getStudents());
System.out.println(onlineCourse.getReviews());
}
}
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;
}
}
public void changeState(State newState) {
this.state = newState;
}
public State getState() {
return state;
}
public List<String> getReviews() {
return reviews;
}
public List<Student> getStudents() {
return students;
}
private boolean availableTo(Student student) {
return student.isEnabledForPrivateClass(this);
}
}
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
private List<OnlineCourse> privateCourses = new ArrayList<>();
public boolean isEnabledForPrivateClass(OnlineCourse onlineCourse) {
return privateCourses.contains(onlineCourse);
}
public void addPrivateCourse(OnlineCourse onlineCourse) {
this.privateCourses.add(onlineCourse);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
패턴 적용 후
public interface State {
void addReview(String review, Student student);
void addStudent(Student student);
}
public class Private implements State {
private OnlineCourse onlineCourse;
public Private(OnlineCourse onlineCourse) {
this.onlineCourse = onlineCourse;
}
@Override
public void addReview(String review, Student student) {
if (this.onlineCourse.getStudents().contains(student)) {
this.onlineCourse.getReviews().add(review);
} else {
throw new UnsupportedOperationException("프라이빗 코스를 수강하는 학생만 리뷰를 남길 수 있습니다.");
}
}
@Override
public void addStudent(Student student) {
if (student.isAvailable(this.onlineCourse)) {
this.onlineCourse.getStudents().add(student);
} else {
throw new UnsupportedOperationException("프라이빛 코스를 수강할 수 없습니다.");
}
}
}
public class OnlineCourse {
private State state = new Draft(this);
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 State getState() {
return state;
}
public List<Student> getStudents() {
return students;
}
public List<String> getReviews() {
return reviews;
}
public void changeState(State state) {
this.state = state;
}
}
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
private Set<OnlineCourse> onlineCourses = new HashSet<>();
public boolean isAvailable(OnlineCourse onlineCourse) {
return onlineCourses.contains(onlineCourse);
}
public void addPrivate(OnlineCourse onlineCourse) {
this.onlineCourses.add(onlineCourse);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
'Computer Science > 디자인 패턴' 카테고리의 다른 글
12.프록시 패턴 (Proxy Pattern) (0) | 2023.11.11 |
---|---|
11. 컴포지트 패턴 (Composite Pattern) (0) | 2023.10.28 |
10. 이터레이터 패턴 (Iterator Pattern) (1) | 2023.10.28 |
9. 템플릿 메소드 패턴 (Template Method Pattern) (0) | 2023.10.16 |
7. 어댑터 패턴 (Adapter Pattern) (0) | 2023.10.08 |
- Total
- Today
- Yesterday
- Spring
- 카카오 코테
- 스프링
- 디자인패턴
- WORE
- 카카오
- jvm
- spring cloud sleuth
- KAKAO 2021
- okhttp3
- 카카오 인턴
- Java #JIT #JVM
- PatternSyntaxException
- 2021
- nginx 내부
- Kakao Blind
- Java
- 카카오코테
- zipkin
- 모던 자바 인 액션
- behavior parameterization
- decorator
- 코테
- 프로그래밍 모델
- 신규 아이디 추천
- 2019 Kakao Blind
- Java #GC #가비지콜렉터 #Garbage Collector
- IOC
- WORA
- 2020 KAKAO
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |