반응형
설명
상태를 객체로 만들어서 각 상태 별 동작을 처리하는 패턴입니다.
클라이언트 코드가 직접 호출하는 Context class에서 상태별로 다른 State 구현객체를 사용하여 상태별로 다른 처리가 가능하도록 합니다.
구성요소
💡 - State Interface
- State Concrete Class : State Instance (implement each State Concrete Class)
- Context Class
- 상태 Interface : 기능만 명세화
- 상태 구현클래스 (Concrete Class) : 각 상태별 기능을 구현
- 컨텍스트 클래스 : 클라이언트 코드에서 직접 호출하는 클래스
- 로직구현코드가 없고, 상태객체를 호출하여 기능을 수행 → 클라이언트 코드에게 결과를 반환한다.
- 필드로 상태 객체 를 가진다.
- 클라이언트 코드(호출하는쪽)가 기능요청 → 상태객체를 활용하여 기능을 처리한다.
왜 사용해야 하는지
- 조건문을 사용하는 코드를 덜쓰려고
- 새론운 상태에 대한 처리가 추가되었을 때, 새로운 상태객체를 구현해서 처리할 수 있기때문에 편함 (안그럼 조건문마다 분기추가해서 코드변경해야됨, 기존 로직을 건드릴 가능성 존재)
예시
티머니 충전기능을 개발해보겠습니다.
티머니의 상태를 인터페이스로 만들었습니다.
public interface State {
void chargeTmoney(int balance, int charging);
void useTmoney(int balance);
void transfer(int balance);
}
그다음 각각 사용가능한티머니, 사용불가능한 티머니 객체를 만듭니다.
public class UnUsableTmoney implements State {
Tmoney tmoney;
public UnUsableTmoney(Tmoney tmoney) {
this.tmoney = tmoney;
}
/**
* 충전 후 잔액 0원 초과
* 사용불가 -> 사용가능
*/
@Override
public void chargeTmoney(int balance, int charging) {
int bal = balance + charging;
System.out.println("================ 사용불가능한 카드 [충전중...] ================");
System.out.println(String.valueOf(charging) + "원 충전합니다");
System.out.println("잔액 : " + String.valueOf(bal));
this.tmoney.setBalance(bal);
this.tmoney.setState(bal > 0 ? tmoney.usable : tmoney.unUsable);
}
/**
* 잔액 0원 : 사용 불가능
*/
@Override
public void useTmoney(int balance) {
System.out.println("================ 사용불가능한 카드 [사용시도] ================");
System.out.println("잔액이 부족합니다.");
this.tmoney.setBalance(balance);
this.tmoney.setState(tmoney.unUsable);
}
/** 중략 **/
}
public class UsableTmoney implements State {
Tmoney tmoney;
public UsableTmoney(Tmoney tmoney) {
this.tmoney = tmoney;
}
/**
* 사용가능 -> 사용가능
*/
@Override
public void chargeTmoney(int balance, int charging) {
int bal = balance + charging;
System.out.println("================ 사용가능한 카드 [충전중......] ================");
System.out.println(String.valueOf(charging) + "원 충전합니다");
System.out.println("잔액 : " + String.valueOf(bal));
this.tmoney = new Tmoney(bal);
this.tmoney.setState(tmoney.usable);
}
/**
* 사용 후 잔액 0원 : 사용불가
*/
@Override
public void useTmoney(int balance) {
int bal = balance - 1000;
System.out.println("================ 사용가능한 카드 [사용] ================");
System.out.println("1000 원 사용합니다");
System.out.println("잔액 : " + String.valueOf(bal));
this.tmoney.setBalance(bal);
this.tmoney.setState(bal > 0 ? tmoney.usable : tmoney.unUsable);
}
/** 중략 **/
}
그다음 티머니 객체에서 상태값들을 위임받아서 사용합니다.
public class Tmoney {
UsableTmoney usable;
UnUsableTmoney unUsable;
State state;
int balance = 0;
public Tmoney(int balance) {
this.balance = balance;
this.usable = new UsableTmoney(this);
this.unUsable = new UnUsableTmoney(this);
this.state = this.balance > 0 ? this.usable : this.unUsable;
}
/**
* 상태를 바꾼다.
* State의 구현체면 상태객체로 사용가능
*/
void setState(State state) {
this.state = state;
}
public void setBalance(int bal) {
this.balance = bal;
}
/**
* 버스를 탄다
* 잔액이 있으면 : 사용가능, 잔액차감
* 잔액이 없으면 : 사용불가, 충전필요
*/
public void takeBus() {
if(this.balance > 0) {
this.usable.useTmoney(this.balance);
} else {
this.unUsable.useTmoney(this.balance);
}
}
/**
* 충전을 한다
* 충전 후 잔액에 따라 상태변경
* todo. 어떤걸 호출하던, 충전 후 잔액 > 0 이어야만 useable
*/
public void charging(int charging) {
if(this.balance > 0) {
usable.chargeTmoney(this.balance,charging);
} else {
unUsable.chargeTmoney(this.balance,charging);
}
}
/**
* 환승을 한다
*/
public void transferTo(){
state.transfer(this.balance);
this.state = this.balance > 0 ? usable : unUsable;
}
}
이제 클라이언트에서 실행하면 됩니다.
public class UsingTmoney {
public static void main(String[] args) {
Tmoney tmoney = new Tmoney(2000); // 2번 사용가능
tmoney.takeBus();
tmoney.transferTo();
tmoney.takeBus();
tmoney.transferTo();
tmoney.takeBus();
tmoney.takeBus();
tmoney.charging(1000);
tmoney.takeBus();
}
}
design-patterns-ex/src/patterns/structural/state/tmoney at master · YINAKIM/design-patterns-ex
티머니 충전상태
상태변화 | 동작 | 조건 | 실행 | 결과 |
돈X → 돈O | 티머니충전 | 충전금액 없음이면 | 충전금액 증가 | 충전금액 사용가능 |
돈O → 돈O | 티머니충전 | 충전금액 사용가능 | 충전금액 증가 | 충전금액 사용가능 |
돈X → 돈X | 티머니 사용 | 충전금액 없음이면 | 사용불가(충전필요) | 충전금액 없음 유지 |
돈O → 돈O | 티머니 사용 | 충전금액 사용가능 | 티머니사용 , 충전금 차감 | 충전금 있으면 사용가능(금액사용) 충전금 없으면 사용불가(충전필요) |
💡 추가요청사항 환승할인 하는 경우 : 티머니 사용 → 충전금 차감 0원
현재 상태에 따라, 기능이 다르게 동작하는 패턴 (코드에서 조건문 사용하게 됨)
참고
https://refactoring.guru/ko/design-patterns/state
반응형
댓글