public class VendingMachine {
    public static enum State { NOCOIN, SELECTABLE }
    
    private State state = State.NOCOIN;
    
    public void insertCoin(int coin) {
        switch(state) {
        case NOCOIN:
            increaseCoin(coin);
            state = State.SELECTABLE;
            break;
        case SELETABLE:
            increaseCoin(coin);
        }
    }
    
    public void select(int productId) {
        switch(state) {
        case NOCOIN:
            break;
        case SELECTABLE:
            provideProduct(productId);
            decreaseCoin();
            if (hasNoCoin()) {
                state = State.NOCOIN;
            }
        }
    }
    ... // increaseCoin, provideProduct, decreaseCoin 구현
}
case SOLDOUT:
    returnCoin();
public interface State {
    State increaseCoin(int coin, VendingMachine vm);
    State select(int productId, VendingMachine vm);
}
public class NoCoin implements State {
    @Override
    public State increaseCoin(final int coin, final VendingMachine vm) {
        vm.increaseCoin(coin);
        if (vm.isCoinEmpty()) {
            return new NoCoin();
        }
        return new Selectable();
    }
    @Override
    public State select(final int productId, final VendingMachine vm) {
        throw new UnsupportedOperationException();
    }
}
public class Selectable implements State {
    @Override
    public State increaseCoin(final int coin, final VendingMachine vm) {
        vm.increaseCoin(coin);
        return this;
    }
    @Override
    public State select(final int productId, final VendingMachine vm) {
        vm.provideProduct(productId);
        vm.decreaseByProductPrice(productId);
        if (vm.isCoinEmpty()) {
            return new NoCoin();
        }
        return this;
    }
}
public class SoldOut implements State {
    @Override
    public State increaseCoin(final int coin, final VendingMachine vm) {
        return new NoCoin();
    }
    @Override
    public State select(final int productId, final VendingMachine vm) {
        throw new UnsupportedOperationException();
    }
}
public class VendingMachine {
    private final static List<Product> items = new ArrayList<>();
    static {
        items.add(new Product(1, 100));
        items.add(new Product(2, 200));
        items.add(new Product(3, 300));
        items.add(new Product(4, 400));
    }
    private State state;
    private int coin; // coin을 state 내부에서 관리하도록 할 수도 있다.
    public VendingMachine() {
        state = StateFactory.create();
    }
    public void insertCoin(final int coin) {
        state = state.increaseCoin(coin, this);
    }
    public void select(final int productId) {
        state = state.select(productId, this);
    }
    public boolean isCoinEmpty() {
        return coin == 0;
    }
    public void increaseCoin(final int coin) {
        this.coin += coin;
    }
    public void decreaseByProductPrice(final int productId) {
        this.coin -= items.get(productId).getPrice();
    }
    public Product provideProduct(final int productId) {
        return items.get(productId);
    }
}
단일 책임 원칙 : State 구현과 사용을 분리개방 폐쇄 원칙 : 새로운 상태가 추가 되어도 이를 사용하는 코드는 변경 필요없음.리스코프 치환 원칙 : 상태 인터페이스에 정의된 메서드를 상태 구현체가 올바른 용도로 재정의하고 있음인터페이스 분리 원칙 : 상태에 따라 사용하는 메서드와 사용하지 않는 메서드가 구분되기 때문에 제대로 지켜지지 않고 있음의존성 역전 원칙 : State 인터페이스를 만들고 사용과 구현 측면에서 모두 인터페이스를 바라보고 있으므로 잘 따르고 있다.상태 패턴은 객체의 상태에 따라 다른 동작을 구현해야할 때 사용할 수 있는 디자인 패턴이다.
전략패턴과 비슷하여 헷갈릴 수도 있지만 전략패턴은 상속의 한계를 해결하면서, 사용자가 캡슐화된 알고리즘 전략을 쉽게 바꿀 수 있도록 유연성을 제공하는 것에 초점이 맞춰져 있지만, 상태 패턴은 한 객체가 보유한 상태에 따라 동작을 다르게 수행해야 할 때 사용한다.
즉, 다시 말해 전략 패턴은 다형성의 주체가 사용자에 있지만, 상태 패턴은 다형성의 주체가 보유한 상태 객체라고 볼 수 있다.