Pattern - State

3 minute read

지금 내가 하는 모든일은 언젠가는 다시 나에게 돌아온다.

의도


객체의 내부 상태에 따라 스스로 행동을 변경할 수 있게 허가하는 패턴으로, 이렇게하면 객체는 마치 자신의 클래스를 바꾸는 것처럼 보입니다.

동기


네트워크 연결을 추상화한 TCPConnection 클래스를 생각해봅시다. TCPConnection 객체를 여러 상태를 가질 수 있습니다. 간단히 예를 들어, 연결 성공 , 대기, 연결 종료 의 상태를 갖습니다. TCPConnection 객체는 다른 객체에서 동일한 요청을 받을 때, 자신이 현재 상태에 따라 다르게 받응합니다. 예를 들어, Open 요청은 연결 상태가 “연결 종료” 또는 “연결 성공” 상태인지에 따라 처리하는 결과가 다릅니다. 이 때 상태 패턴을 사용합니다. 상태 패턴을 사용하면 TCPConnection 클래스가 각 상태에서 어떨게 다르게 반응하는지 관리할 수 있습니다.

활용성


  • 객체의 행동이 상태에 따라서 달라질 수 있고, 객체의 상태에 따라서 런타임에 행동이 바뀌어야 합니다.
  • 어떤 연산에 그 객체의 상태에 따라 달라지는 다중 분기 조건 처리가 너무 많이 들어 있을 때. 객체의 상태를 표현하기 위해 상태를 하나 이상이 나열할 상수로 정의해야합니다. 동일한 조건 문장들을 하나 이상의 연산에 중복 정의해야 할 때도 잦습니다. 이때, 객체의 상태를 별도의 객체로 정의하면, 다른 객체들과 상관없이 그 객체의 상태를 다양화 시킬 수 있습니다.

항목에 대한 설명


  • Context
    TCPConnection
    사용자가 관심 있는 인터페이스를 정의합니다. 객체의 현재 상태를 정의한 ConcreteState 서브 클래스의 인스턴스를 유지, 관리합니다.

  • State
    TCPState
    Context 의 각 상태 별로 필요한 행동을 캡슐화하여 인터페이스로 정의합니다.

  • ConcreteState 서브 클래스들
    TCPEstablished, TCPListen, TCPClosed
    각 서브 클래스들은 Context 의 상태에 따라 처리되어야 할 실제 행동을 구현합니다.

협력 방법


  • 상태에 따라 다른 요청을 받으면 Context 클래스는 현재의 ConcreteState 객체로 전달합니다. 이 ConcreateState 클래스의 객체는 State 클래스를 상속하는 서브 클래스들 중 하나의 인스턴스 일 것이다.
  • Context 클래스는 실제 연산을 처리할 State 객체에 자신을 매개변수로 전달합니다. 이로써 State 객체는 Context 클래스에 정의된 정보에 접근할 수 있게 됩니다.
  • Context 클래스는 사용자가 사용할 수 있는 기본 인터페이스를 제공합니다. 사용자는 상태 객체를 Context 객체와 연결 시킵니다. 즉, Context 클래스에 현재 상태를 정의하는 것입니다. 이렇게 Context 객체를 만들고 나면 사용자는 State 객체를 직접 다루지 않고 Context 객체에 요청을 보내기만 하면 됩니다.
  • Context 클래스 또는 ConcreteState 서브클래스들은 자기 다음의 상태가 무엇이고, 어떤 환경에서 다음 상태로 가는지 결정할 수 있습니다. 즉, 상태는 상태 전의 규칙이 있으므로, 각각 한 상태에서 다른 상태로 전이하는 규칙을 알아야 합니다.

결과


  • 상태에 따른 행동을 국소화하며, 서로 다른 상태에 대한 행동을 별도의 객체로 관리합니다.
  • 상태 전이를 명확하게 만듭니다.
  • 상태 객체는 공유될 수 있습니다. 상태는 단지 타입으로만 표현되므로, State 객체는 인스턴스 변수 없이 여러 Context 클래스의 인스턴스로도 객체를 공유할 수 있습니다. 상태가 이런 식으로 공유될 때, 이 공유된 상태는 실질적으로 플라이급 객체라고 해도 됩니다. 행동만 있는 플라이급 객체를 말하는 것이다.

코드 예제 – 기본 샘플



package designpattern.gof_state.sample03;

import designpattern.gof_state.sample03.person.ChoiHakyu;
import designpattern.gof_state.sample03.person.Person;

public class Ground {
    public static void main(String[] args) {

        Person person = new ChoiHakyu();

        person.checkMyStatus();

        person.applyStatus("규완이가 중독시켰다.");
        person.checkMyStatus();
        person.applyStatus("훈의가 고쳤다.");
        person.checkMyStatus();
        person.applyStatus("승화가 다리를 부려뜨렸다." );
        person.checkMyStatus();
    }
}   


package designpattern.gof_state.sample03.person;

import designpattern.gof_state.sample03.states.HealthyState;
import designpattern.gof_state.sample03.states.LegBrokenState;
import designpattern.gof_state.sample03.states.PoisonedState;
import designpattern.gof_state.sample03.states.State;

public class ChoiHakyu implements Person {

    private State state;
    private static final State healtyState = new HealthyState();
    private static final State legBrokenState = new LegBrokenState();
    private static final State poisonState = new PoisonedState();

    public ChoiHakyu(){
        this.state = healtyState;
    }

    @Override
    public void applyStatus(String type) {
        switch (type){
            case "훈의가 고쳤다." :
                this.state = healtyState;
                break;
            case "규완이가 중독시켰다." :
                this.state = poisonState;
                break;
            case "승화가 다리를 부려뜨렸다." :
                this.state = legBrokenState;
                break;
        }
    }

    @Override
    public void checkMyStatus() {
        state.Run();

        state.Laydown();

        state.Walk();

        state.Coding();
    }
}

package designpattern.gof_state.sample03.person;

public interface Person {

    public void applyStatus(String type);

    public void checkMyStatus();

}

package designpattern.gof_state.sample03.states;

public class HealthyState implements State {
    @Override
    public void Walk() {
        System.out.println("건강해서 잘 걷는다.");
    }

    @Override
    public void Laydown() {
        System.out.println("건강해서 잘 드러눕는다.");
    }

    @Override
    public void Coding() {
        System.out.println("건강해서 코딩을 열심히 한다.");
    }

    @Override
    public void Run() {
        System.out.println("건강해서 잘 뛴다.");
    }
}

package designpattern.gof_state.sample03.states;

public class LegBrokenState implements State {
    @Override
    public void Walk() {
        System.out.println("다리가 부러져서 더이상 걸을 수 없다.");
    }

    @Override
    public void Laydown() {
        System.out.println("다리가 부러졌지만 누울 수 있다.");
    }

    @Override
    public void Coding() {
        System.out.println("다리가 부러졌지만 손이 안부러져서 코딩은 할 수 있다.");
    }

    @Override
    public void Run() {
        System.out.println("다리가 부러져서 뛰지를 못한다.");
    }
}

package designpattern.gof_state.sample03.states;

public class PoisonedState implements State {
    @Override
    public void Walk() {
        System.out.println("독에 중독되어서 걸으면서 독을 퍼뜨린다.");
    }

    @Override
    public void Laydown() {
        System.out.println("독에 중독되어서 누어 있어야 한다.");
    }

    @Override
    public void Coding() {
        System.out.println("독에 중독되었지만 일정이 빠듯해 코딩을 한다.");
    }

    @Override
    public void Run() {
        System.out.println("독에 중독되었지만 달릴 수 있다.");
    }
}

package designpattern.gof_state.sample03.states;

public interface State {

    public void Run();

    public void Walk();

    public void Laydown();

    public void Coding();

}