해당 포스팅은 한빛 미디어 헤드퍼스트 디자인 패턴(에릭 프리먼, 엘리자베스 롭슨 저)를 통해 공부한 내용을 정리한 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
좋은 프로그래머는 자기 두뇌를 사용한다. 그러나 좋은 가이드라인은 모든 케이스를 고려해야만 하는 노력을 줄여준다. (Francis Glassborow, 개발자)
전략 패턴과 상태 패턴은 쌍둥이
상태 다이어그램과 비슷
다른 상태로 전환을 위해서는 어떤 동작이 필요
예외 케이스에 대해서도 항상 잘 생각해야함.
상태 값을 저장하는 인스턴스 변수를 만들고 메소드 내에서 조건문을 써서 다양한 상태를 처리
State Diagram
상태
동전 없음
동전 있음
알맹이 없음
알맹이 판매
상태를 저장하는 인스턴스 변수 정의
/*
* 알맹이 상태 저장
*/finalstaticint SOLD_OUT = 0; // 알맹이 없음finalstaticint NO_QUARTER = 1; // 동전 없음 finalstaticint HAS_QUARTER = 2; // 동전 있음 finalstaticint SOLD = 3; // 알맹이 내보내는 중
시스템에서 일어날 수 있는 모든 행동 정의
동전투입
동전 반환
손잡이 돌림
알맹이 내보냄
클래스 만들기
publicclassGumballMachine{
/*
* 알맹이 상태 저장
*/finalstaticint SOLD_OUT = 0; // 알맹이 없음finalstaticint NO_QUARTER = 1; // 동전 없음finalstaticint HAS_QUARTER = 2; // 동전 있음finalstaticint SOLD = 3; // 알맹이 내보내는 중int state = SOLD_OUT;
int count = 0;
publicGumballMachine(int count){
this.count = count;
if (count > 0) {
state = NO_QUARTER;
}
}
/*
* 동전이 투입된 경우
*/publicvoidinsertQuarter(){
if (state == HAS_QUARTER) {
System.out.println("동전은 하나만 넣어주세요"); // 동전이 이미 투입되어 있다면 이미 있다고
} elseif (state == NO_QUARTER) {
state = HAS_QUARTER;
System.out.println("동전을 넣으셨습니당");
} elseif (state == SOLD_OUT) {
System.out.println("매진되었습니다. 다음 기회에 이용해주세요");
} else { // state == SOLD
System.out.println("알맹이를 내보내는 중");
}
}
// 이후 부터는 행동 정의 생략.../*
* 동전 반환
*/publicvoidejectQuarter(){
}
/*
* 손잡이를 돌리는 경우
*/publicvoidturnCrank(){
}
/*
* 알맹이 내보내기
*/publicvoiddispense(){
}
publicvoidrefill(int numGumBalls){
}
}
뽑기 기계 수정 요청
새로운 기능 추가 요구
현재 코드 ⇒ 관리 및 수정의 어려움
상태 별 행동을 별도의 클래스에 넣어두고 모든 상태에 대해 각각 자기가 할 일을 구현
⇒ 바뀌는 부분을 캡슐화
뽑기 기계가 현재 상태를 나타내는 상태 객체에게 작업을 넘기도록 ⇒ 구성
새로운 디자인 구성하기
기존 코드를 그대로 활용하는 대신 상태 객체들을 별도의 코드에 넣고 어떤 행동이 일어나면 현재 상태 객체에서 필요한 작업을 처리.
뽑기 기계와 관련된 모든 행동에 관한 메소드가 들어있는 State 인터페이스 정의
기계의 모든 상태를 대상으로 상태 클래스 구현.
상태에 대해서는 상태 클래스가 모든 책임을 갖도록
조건문 코드 없애고 상태 클래스에 작업 위임.
상태 패턴 적용
State Pattern
구조 살펴보기
해당 결과 도출
각 상태의 행동을 별개의 클래스로 국지화
관리하기 힘든 분기문 삭제
상태를 변경에는 닫혀있고 새로운 상태를 추가하는 확장에는 열려있도록. (OCP적용)
이해하기 좋은 코드 베이스와 클래스 구조 적용
publicclassGumballMachine{
/*
* 알맹이 상태 저장
*/
State soldOutState; // 알맹이 없음
State noQuarterState; // 동전 없음
State hasQuarterState; // 동전 있음
State soldState; // 알맹이 내보내는 중
State state;
int count = 0;
publicGumballMachine(int count){
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
this.count = count;
if (count > 0) {
state = noQuarterState;
} else {
state = soldOutState;
}
}
/*
* 동전이 투입된 경우
*/publicvoidinsertQuarter(){
state.insertQuarter();
}
/*
* 동전 반환
*/publicvoidejectQuarter(){
state.ejectQuarter();
}
/*
* 손잡이를 돌리는 경우
*/publicvoidturnCrank(){
state.turnCrank();
state.dispense();
}
// State 생성voidsetState(State state){
this.state = state;
}
}
// State 예시publicinterfaceState{
// 동전을 넣었을 때의 상태 정의publicvoidinsertQuarter();
// 동전을 반환했을 때의 상태 정의publicvoidejectQuarter();
// 손잡이를 돌렸을 때의 상태 정의publicvoidturnCrank();
// 알맹이가 나올 때의 상태 정의publicvoiddispense();
publicvoidrefill();
}
publicclassHasQuarterStateimplementsState{
GumballMachine gumballMachine;
publicHasQuarterState(GumballMachine gumballMachine){
this.gumballMachine = gumballMachine;
}
@OverridepublicvoidinsertQuarter(){
System.out.println("동전을 한개만 넣어주세요");
}
@OverridepublicvoidejectQuarter(){
System.out.println("동전을 반환합니다");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
@OverridepublicvoidturnCrank(){
System.out.println("손잡이를 돌리셨습니다.");
gumballMachine.setState(gumballMachine.getSoldState());
}
@Overridepublicvoiddispense(){
System.out.println("동전을 넣어주세요");
}
publicvoidrefill(){}
}
State Pattern
State Pattern
💡 상태 패턴 객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있음. 클래스가 바뀌는 것과 같은 결과