

OnlineCourseState 열거형: DRAFT, PUBLISHED, PRIVATE의 상태를 관리.addReview: 상태에 따라 리뷰 작성 가능 여부를 결정.addStudent: 상태에 따라 학생 등록 가능 여부를 결정.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);
}
}
StudentisEnabledForPrivateClass로 확인.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 + '\'' +
'}';
}
}
Clientpublic 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());
}
}
코드 복잡도 증가:
OnlineCourse 내부에 몰려 있어 코드가 비대해지고 복잡해짐.addReview 메서드: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("리뷰를 작성할 수 없습니다.");
}
}캡슐화 부족:
OnlineCourse에 몰려 응집도가 낮음.확장성 문제:

상태 인터페이스 (State) 도입:
addReview와 addStudent를 State 인터페이스로 분리하여 상태별 행동 정의.상태별 클래스:
Draft, Published, Private 클래스로 상태별 행동을 캡슐화.상태 전환:
changeState 메서드를 통해 동적으로 변경.캡슐화 강화:
OnlineCourse는 상태 전환과 상태 실행을 위임하며, 코드가 단순해짐.State 인터페이스public interface State {
void addReview(String review, Student student);
void addStudent(Student student);
}
Draft 상태Private 상태로 전환.public class Draft implements State {
private OnlineCourse onlineCourse;
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));
}
}
}
Published 상태public class Published implements State {
private OnlineCourse onlineCourse;
public Published(OnlineCourse onlineCourse) {
this.onlineCourse = onlineCourse;
}
@Override
public void addReview(String review, Student student) {
this.onlineCourse.getReviews().add(review);
}
@Override
public void addStudent(Student student) {
this.onlineCourse.getStudents().add(student);
}
}
Private 상태isAvailable)을 만족해야 가능.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("프라이빗 코스를 수강할 수 없습니다.");
}
}
}
OnlineCoursepublic 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;
}
}
장점
• 상태에 따른 동작을 개별 클래스로 옮겨서 관리할 수 있다.
• 기존의 특정 상태에 따른 동작을 변경하지 않고 새로운 상태에 다른 동작을 추가할 수 있다.
• 코드 복잡도를 줄일 수 있다.
단점
• 복잡도가 증가한다.

BlueLightRedLight 클래스speed)에 따라 blueLight()와 redLight() 메서드의 출력이 달라진다.if-else 조건문으로 구현되어 있다.public class BlueLightRedLight {
private int speed;
public BlueLightRedLight(int speed) {
this.speed = speed;
}
public void blueLight() {
if (speed == 1) {
System.out.println("무 궁 화 꽃 이");
} else if (speed == 2) {
System.out.println("무궁화꽃이");
} else {
System.out.println("무광꼬치");
}
}
public void redLight() {
if (speed == 1) {
System.out.println("피 었 습 니 다.");
} else if (speed == 2) {
System.out.println("피었습니다.");
} else {
System.out.println("피어씀다");
}
}
}
문제점
조건문 남용:
if-else 조건문이 계속 늘어나 코드가 복잡해진다.확장성 부족:
행동과 속도의 결합:
BlueLightRedLight 클래스 내부에 결합되어 있어 재사용성이 떨어진다.바뀐 점

Normal, Faster, Fastest)을 Speed 인터페이스로 추상화했다.BlueLightRedLight는 전략 실행을 담당하는 컨텍스트(Context)로, 알고리즘 실행만 책임지고 특정 알고리즘(속도 동작)을 알지 못한다.코드 분석
Speed 인터페이스Normal, Faster, Fastest)는 속도별 동작을 정의한다.public interface Speed {
void blueLight();
void redLight();
}
Normal, Faster, Fastest)는 Speed 인터페이스를 구현하며, 속도에 따른 동작을 정의한다.public class Normal implements Speed {
@Override
public void blueLight() {
System.out.println("무 궁 화 꽃 이");
}
@Override
public void redLight() {
System.out.println("피 었 습 니 다.");
}
}
public class Faster implements Speed {
@Override
public void blueLight() {
System.out.println("무궁화꽃이");
}
@Override
public void redLight() {
System.out.println("피었습니다.");
}
}
public class Fastest implements Speed{
@Override
public void blueLight() {
System.out.println("무광꼬치");
}
@Override
public void redLight() {
System.out.println("피어씀다.");
}
}
BlueLightRedLight 클래스Speed 인터페이스를 사용하여 동작을 실행하며, 특정 구현체에 의존하지 않는다.Speed 구현체만 만들면 된다.public class BlueLightRedLight {
public void blueLight(Speed speed) {
speed.blueLight();
}
public void redLight(Speed speed) {
speed.redLight();
}
}
Client에서 익명 클래스를 활용하여 동적으로 새로운 동작을 정의할 수도 있다.public class Client {
public static void main(String[] args) {
BlueLightRedLight game = new BlueLightRedLight();
game.blueLight(new Normal());
game.redLight(new Fastest());
game.blueLight(new Speed() {
@Override
public void blueLight() {
System.out.println("blue light");
}
@Override
public void redLight() {
System.out.println("red light");
}
});
}
}
1) BlueLightRedLight가 blueLight(Speed speed) 호출.
2) 전달받은 Speed 구현체의 blueLight() 실행.
(결과 출력: 예: Normal 실행 시 무 궁 화 꽃 이 출력.)
3) 클라이언트에서 동적으로 다른 전략(Faster, Fastest) 실행.
4) 런타임 중 익명 클래스를 활용해 새로운 동작 추가.
바뀐점
조건문 제거:
if-else 조건문이 사라지고, 각 동작이 Speed 구현체로 분리되었다.확장성 향상:
Speed 인터페이스를 구현하는 클래스만 추가하면 된다.유연한 전략 선택:
컨텍스트 단순화:
BlueLightRedLight 클래스는 특정 동작을 실행할 책임만 가지며, 구체적인 구현에 대해 알지 못한다.장점
단점
Comparator 인터페이스:public class StrategyInJava {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(5);
System.out.println("Before Sorting: " + numbers);
// 전략 설정: Comparator.naturalOrder()
Collections.sort(numbers, Comparator.naturalOrder());
System.out.println("After Sorting: " + numbers);
}
}
Collections.sort()는 Comparator 객체를 받아 정렬 전략을 실행.naturalOrder, 내림차순: reverseOrder, 사용자 정의)을 런타임에서 선택 가능.ApplicationContext:ClassPathXmlApplicationContext, FileSystemXmlApplicationContext, AnnotationConfigApplicationContext)가 전략으로 작동.PlatformTransactionManager:public class StrategyInSpring {
public static void main(String[] args) {
// 다양한 ApplicationContext 구현체
ApplicationContext applicationContext = new ClassPathXmlApplicationContext();
ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext();
ApplicationContext applicationContext2 = new AnnotationConfigApplicationContext();
// 스프링에서 사용하는 다양한 전략 인터페이스
BeanDefinitionParser parser;
PlatformTransactionManager platformTransactionManager;
CacheManager cacheManager;
}
}
컨텍스트의 전략적 역할:
ApplicationContext 인터페이스를 구현한 다양한 구현체를 사용하여 애플리케이션 환경에 맞는 IoC 컨테이너를 선택 가능.트랜잭션 관리 전략:
PlatformTransactionManager를 통해 JDBC, JPA 등 특정 기술에 종속되지 않고 트랜잭션 관리.캐싱 전략:
CacheManager를 사용해 다양한 캐싱 기술(EhCache, ConcurrentMapCache 등)을 적용.