본 게시물은 스스로의 공부를 위한 글입니다.
잘못된 내용이 있으면 댓글로 알려주세요!
코드를 통해 상태 패턴을 알아보자.
public class VendingMachine {
public static enum State {NOCOIN, SELECTABLE}
private State state = State.NOCOIN;
public void insertCoin(int coin) {
switch (state) {
case NOCOIN:
// 로직
case SELECTABLE:
// 로직
}
}
public void changeState(State state){
this.state=state;
}
}
insertCoin(coin)
의 로직이 달라진다.insertCoint(coin)
로직에 switch case를 추가한다.맙소사.. 상태를 추가하거나 로직을 변경할 때마다 코드를 변경해야 하다니?
심지어 switch 문이 길어진다고?
public class VendingMachine {
private State state;
public VendingMachine() {
state = new NoCoinState();
}
public void insertCoin(int coin) {
state.increaseCoin(coin, this); // 상태 객체에게 코드 구현을 위임
}
public void changeState(State state) {
this.state=state;
}
}
public interface State {
public void increaseCoin(int coin, VendingMachine vm);
}
public class NoCoinState implements State {
@Override
public void increaseCoin(int coin, VendingMachine vm) {
//로직 ...
vm.changeState(new SelectableState());
}
}
만약 자판기에 청소 상태 구현을 위해 State가 추가되었더라면?
CleaningState
클래스를 추가하기만 하면 된다. 그럼 VendingMachine
클래스의 코드는 그대로 유지된다.
또한 상태에 따른 로직의 변경이 일어나더라도 해당 상태의 클래스만 변경해주면 된다. VendingMachine
클래스는 건들 필요없다.
물론 단점도 존재한다. 클래스 개수가 증가하기 때문에 상태 구조를 모른다면 유지보수가 어려울 수 있다.
위 코드를 다시 보자
public class NoCoinState implements State {
@Override
public void increaseCoin(int coin, VendingMachine vm) {
//로직 ...
vm.changeState(new SelectableState()); //컨텍스트의 상태 변경
// 만약 컨텍스트 자체에서 상태 변경을 한다면 VendingMachine을 인자로 받지 않아도 된다.
}
}
상태 객체가 VendingMachine
을 인자로 받아 상태 변경을 하고 있다.
컨텍스트의 상태를 변경할 때, 컨텍스트의 다른 값에 접근해야 할 때도 있다.
따라서 컨텍스트의 메서드를 public
으로 추가해야 한다. (예를 들면 동전 개수에 접근하는 getCoin()
메서드 추가)
하지만 컨텍스트 자체가 상태를 변경한다면? 메서드를 추가해야 하는것은 마찬가지지만 priavte
으로 선언 가능하다. (간단한 필드 접근이라면 메서드도 필요 없다.)
이처럼 컨텍스트의 상태 변경을 누가 할지는 주어진 상황에 알맞게 정해 주어야 한다.
컨텍스트 자체에서 상태 변경이 유리한 경우는 다음과 같다.
반면 상태 객체에서 컨텍스트의 상태를 변경하는 경우 컨텍스트에 영향을 주지 않으면서 상태를 추가하거나 상태 변경 규칙을 바꿀 수 있다는 장점이 있다.
전략 패턴과 아주 유사하다.
차이점은 객체의 상태 속성을 변환하는데 상태 패턴은 스스로 변환 할 수 있지만, 전략 패턴은 외부에서 새로운 상태의 주입이 필요하다.
또한 전략 패턴은 하나의 특정 작업만 처리하는 반면 상태 패턴은 컨텍스트 개체가 수행하는 대부분의 모든 것에 대한 기본 구현을 제공한다.
만약 VendingMachine
클래스를 전략 패턴으로 작성했다면 다음과 같을 것이다.
public class VendingMachine {
private State state;
public VendingMachine(State state) { //생성 시 상태 결정. 변경 불가
this.state = state;
}
public void insertCoin(int coin) {
state.increaseCoin(coin, this); // 상태 객체에게 코드 구현을 위임하는건 동일
}
}
또는
public class VendingMachine {
public void insertCoin(int coin, State state) { // 메서드 사용시 계속 파라미터로 넘겨주기
state.increaseCoin(coin, this); // 상태 객체에게 코드 구현을 위임하는건 동일
}
}
개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴(최범균)