State Pattern을 이용하면 객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있습니다. 마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있습니다.
(Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.)
State 패턴은 상태(state)를 클래스로 표현한다. 현실세계에서 상태를 사물(object)로 보는 일은 거의 없기 때문에, 상태를 클래스로 표현하는 일은 어색할 수 있다. 그러나, 상태를 클래스로 표현하면 클래스의 교체를 통해서 ‘상태의 변화’를 나타낼 수 있고, 새로운 상태를 추가해야 하는 경우에는 무엇을 프로그램 하면 되는지 명확해진다.
예제) '마이너스계좌'(MinusAccount)를 사용하는 사용자의 잔금(balance)의 변화에 따라서 계좌의 상태값이 변화한다. Low는 -금액이 되었을때 상태이며 더이상 출금이 안됨, Middle는 0~99사이의 금액일때 상태, High는 100이상의 금액일때 상태이다.
public abstract class AState {
private double balance;
private MinusAccount minusAccount;
public AState(MinusAccount minusAccount) {
this.minusAccount = minusAccount;
}
protected abstract void deposit(double balance);
protected abstract void withdraw(double balance);
Getter/ Setter …
--------------------------------------------------------------------
public class Low extends AState {
public Low(MinusAccount minusAccount) {
super(minusAccount);
}
protected void deposit(double balance) {
setBalance(getBalance() + balance);
if (getBalance() >= 0)
getMinusAccount().setState(
getMinusAccount().getMiddle(), getBalance());
}
protected void withdraw(double balance) {
System.out.print("*출금불가*,");
}
--------------------------------------------------------------------
public class Middle extends AState {
public Middle(MinusAccount minusAccount) {
super(minusAccount);
}
protected void deposit(double balance) {
setBalance(getBalance() + balance);
if (getBalance() >= 99)
getMinusAccount().setState(
getMinusAccount().getHigh(), getBalance());
}
protected void withdraw(double balance) {
setBalance(getBalance() - balance);
if (getBalance() < 0)
getMinusAccount().setState(
getMinusAccount().getLow(), getBalance());
}
--------------------------------------------------------------------
public class High extends AState {
public High(MinusAccount minusAccount) {
super(minusAccount);
}
protected void deposit(double balance) {
setBalance(getBalance() + balance);
}
protected void withdraw(double balance) {
setBalance(getBalance() - balance);
if (getBalance() < 100)
getMinusAccount().setState(
getMinusAccount().getMiddle(), getBalance());
}
--------------------------------------------------------------------
public class MinusAccount {
private Low low;
private Middle middle;
private High high;
private AState state;
public MinusAccount() {
low = new Low(this);
middle = new Middle(this);
high = new High(this);
state = middle;
}
public void deposit(double balance) {
System.out.print("전[" + state.getClass().getName() + "],");
state.deposit(balance);
System.out.println("후[" + state.getClass().getName() + "], +"
+ balance + ", balance[" + state.getBalance() + "]");
}
public void withdraw(double balance) {
System.out.print("전[" + state.getClass().getName() + "],");
state.withdraw(balance);
System.out.println("후[" + state.getClass().getName() + "], -"
+ balance + ", balance[" + state.getBalance() + "]");
}
Getter/ Setter …
--------------------------------------------------------------------
public class Client {
public static void main(String[] args) {
MinusAccount minusAccount = new MinusAccount();
minusAccount.deposit(10);
// 전[Middle],후[Middle], +10.0, balance[10.0]
minusAccount.withdraw(30);
// 전[Middle],후[Low], -30.0, balance[-20.0]
minusAccount.withdraw(5);
// 전[Low],*출금불가*,후[Low], -5.0, balance[-20.0]
minusAccount.deposit(100);
// 전[Low],후[Middle], +100.0, balance[80.0]
minusAccount.deposit(150);
// 전[Middle],후[High], +150.0, balance[230.0]
구현관련
- MinusAccount에서 state값을 map으로 들고 있고 map의 key값인 string으로 조회하면 매번 new해서 객체를 생성하지 않아도 될 듯... 아래의 '다른 예' 참고
다른 예