상태 패턴이란?

행위 패턴 중 하나
상태를 객체화 한 패턴
객체의 내부 상태가 바뀜에 따라서 행위를 변경하는 패턴

 

패턴 적용 전

먼저 "1. 걷기 2. 뛰기 3. 종료" 이런 기능의 프로그램이 있다고 가정하자

String command = "";
while(command.equals("3"){
	command = scannser.nextLine();
    if (command.equals("1")) {
    	if (!player.getState().equals("걷기") && !player.getState().equals("앉아있기")) {
        	player.setState("걷기");
        }
    } else if (command.equals("2")) {
    	if (!player.getState().equals("뛰기") && !player.getState().equals("앉아있기")) {
        	player.setState("뛰기");
        }
    }
}

억지스럽지만 아주 간단하게 이렇게 분기를 할 수 있다. 더 많은 요구사항이 생기게 된다면 이 분기는 어마어마하게 커질 것이다.

이때 적용할 수 있는 패턴은 상태 패턴이다

 

패턴 적용 후

public abstract class State {
    protected Player player;

    public State(Player player) {
        this.player = player;
    }

    abstract void standUp();

    abstract void sitDown();

    abstract void walk();

    abstract void run();

    abstract String getDescription();
}

public class WalkState extends State {

    public WalkState(Player player) {
        super(player);
    }

    @Override
    void standUp() {
        player.setSpeed(0);
        player.talk("멈춰");
        player.setState(new StandUpState(player));
    }

    @Override
    void sitDown() {
        player.setSpeed(0);
        player.talk("걷다가 앉으면 넘어질 수 있어요");
        player.setState(new SitDownState(player));
    }

    @Override
    void walk() {
        player.talk("걷는게 좋아");
    }

    @Override
    void run() {
        player.setSpeed(20);
        player.talk("걷다가 뛰면 빨리 뛸 수 있지");
        player.setState(new RunState(player));
    }

    @Override
    String getDescription() {
        return "걷는 중";
    }
}

public class StandUpState extends State {

    public StandUpState(Player player) {
        super(player);
    }

    @Override
    void standUp() {
        player.talk("언제 움직여?");
    }

    @Override
    void sitDown() {
        player.setState(new SitDownState(player));
        player.talk("앉으니까 편해요");
    }

    @Override
    void walk() {
        player.setSpeed(5);
        player.setState(new WalkState(player));
        player.talk("걷기는 아주 좋아요");
    }

    @Override
    void run() {
        player.setSpeed(10);
        player.setState(new RunState(player));
        player.talk("뛰니까 힘들어");
    }

    @Override
    String getDescription() {
        return "제자리에 서 있어요";
    }
}

public class RunState extends State {

    public RunState(Player player) {
        super(player);
    }

    @Override
    void standUp() {
        player.talk("뛰다가 서면 다쳐");
        player.setSpeed(0);
        player.setState(new StandUpState(player));
    }

    @Override
    void sitDown() {
        player.talk("뛰다가 앉으라고?");
        player.setSpeed(0);
        player.setState(new StandUpState(player));
    }

    @Override
    void walk() {
        player.talk("속도를 줄일게요");
        player.setSpeed(8);
        player.setState(new WalkState(player));
    }

    @Override
    void run() {
        player.talk("더 빨리 뛰라는 얘기지?");
        player.setSpeed(player.getSpeed() + 2);
    }

    @Override
    String getDescription() {
        return "뛰는 중";
    }
}

public class SitDownState extends State {

    public SitDownState(Player player) {
        super(player);
    }

    @Override
    void standUp() {
        player.setState(new StandUpState(player));
        player.talk("일어나자");
    }

    @Override
    void sitDown() {
        player.talk("계속 앉아있네");
    }

    @Override
    void walk() {
        player.talk("앉아서 못 걸으니 일단 서자");
        player.setState(new StandUpState(player));
    }

    @Override
    void run() {
        player.talk("앉아서 못 뛰니 일단 서자");
        player.setState(new RunState(player));
    }

    @Override
    String getDescription() {
        return "앉아있음";
    }
}

 

그래서 상태 패턴을 언제 사용할까

다중 분기 조건이 너무 많을 때 ❗️❗️❗️

 

장점

  1. 상태에 따른 행동을 축소시키고 서로 다른 상태에 대한 행동을 별도의 객체로 관리
  2. 새로운 상태가 추가되더라도 context 코드 영향이 적다 (context 코드는 위에선 player)

 

단점

  1. 상태 클래스들이 많아질수록 변경 규칙을 쉽게 파악하기가 어렵다
  2. 상태끼리 의존도가 발생할 수도 있다

 

상태 패턴과 전략 패턴의 차이

두 패턴은 너무나도 비슷하게 생겼다. 디자인 패턴을 공부해보면 대부분 많이 비슷한 것 같다.

상태 패턴은 상태 객체 내부에서 다음 상태를 결정
전략 패턴은 클라이언트에서 다음에 실행할 객체를 지정할 수 있다

즉, 로직이 수행되면서 행동 또는 상태의 변화가 외부의 개입이 필요한지, 특정 구현 클래스에서 알아서 하는지가 중요하다

복사했습니다!