Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

JAVA Developer Training

스테이트 패턴(State Pattern) 본문

디자인패턴

스테이트 패턴(State Pattern)

Romenest 2021. 10. 5. 14:19
스테이트 패턴( State Pattern )

스테이트 패턴, 즉 상태패턴은 규칙에따라 객체의 상태를 변화시켜 객체가 할 수 있는 행위를 바꾸어주는 패턴을 말한다.

 

스테이트 패턴의 사용 이유

상태 패턴은 객체의 상태를 효율적으로 관리 할 수 있도록 도와준다.

보통 코드를 짰을 경우 각 객체들은 상태값이 변경됨에 따라서 다른 일을 수행하는데

이를 객체에 어떤 동작이 수행될때 마다 상태를 파악하여 그에 맞는 동작을 수행하게 한다면

굉장히 코드가 길어지고 난잡해질 것이며, 수많은 조건문들을 걸어 주어야 할 것이다.

이를 스테이트 패턴을 이용해서 더 나은 코드를 만든다면, 공통된 상태 인터페이스를 만들고 각 상태에 따라 객체를 생성한후 상세 동작을 상태 객체에서 수행하도록 설정하는 것이 되겠다.

 

 

예시

우리가 사용하는 컴퓨터를 예로 들어보자

우리 컴퓨터는 아래와 같은 코드를 가질 수 있다.

public class PC {
    private int ON = 1;
    private int OFF = 0;
    private int state;
    
    public PC() {
        this.state = OFF;
    }
    
    public void onPC() {
        if (state != ON) {
            state = ON;
        }
    }
    
    public void offPC() {
        if (state != OFF) {
            state = OFF;
        }
    }
     
}

기본적으로 컴퓨터는 꺼져있으며

state값이 0이 아니라면 값을 1로 바꾸어주고 

state값이 1이 아니라면 값을 0으로 바꾸어준다.

 

기존의 PC상태는 이렇게 정의해 줄 수 있겠다.

 

여기에 특수한 케이스인 PC오류를 해결하기위해 안전모드로 진입할때 우리는 부팅중 [F2] 키나 [Del]키를 눌러 바이오스창에 진입한다. 라는 것을 추가한다 생각해보자.

*이때 바이오스 진입 상태의 값은 2로 임의로 지정했다.

 

그렇다면 우리는

private int PC_BIOS = 2;
 
public void onCar() {
    if (state == ON) {
        this.state = PC_BIOS;
    } else if (state == PC_BIOS ) {
        this.state = ON; 
    } else if (state != ON) {
        state = ON;
    }
}

위와 같이 상태값이 추가될 때마다 조건을 걸어 수정을 해주어야 한다.

 

이를 해결하기위해 스테이트 패턴을 이용하여 현재 객체인 (PC)가 어떤 상태던지 상관없게 구성하고

상태값의 변화에 독립적이도록 코드를 수정해야한다.

 

  • PC 객체에서는 상태를 나타내는 State를 구현한 객체를 보유하고있어야하고
  • PC 객체에서 onPC, offPC 동작을 수행할 때마다 state에게 자신을 전달하여 상태를 변경해야한다.

 

public class PC {
    private PCState state;
    
    public PC() {
        this.state = offPC.getInstance();
    }
    
    public PCState getState() {
        return state;
    }
 
    public void setState(PCState state) {
        this.state = state;
    }
 
    public void onPC() {
        this.state.on_buutton_pushed(this);
    }
    
    public void offPC() {
        this.state.off_button_pushed(this);
    } 
    public void PCBios() {
        this.state.F2_button_pushed(this);
    }    
}

PC

public interface PCState {
    void on_buutton_pushed(PC pc);
    void off_button_pushed(PC pc);
    void F2_button_pushed(PC pc);
}

상태 객체를 구현하기위한 인터페이스

 

// 켜진 PC
public class onPC implements PCState {
    
    private static onPC instance = new onPC();
    
    public static onPC getInstance() {
        return instance;
    }
 
    @Override
    public void F2_button_pushed(PC pc) {
        pc.setState(PcBios.getInstance());
    }
 
    @Override
    public void off_button_pushed(PC pc) {
        pc.setState(offPC.getInstance());
    }
 
}

// 꺼진 PC
public class offPC implements PCState {
    private static offPC instance = new offPC();
    
    public static offPC getInstance() {
        return instance;
    }
    
    @Override
    public void on_buutton_pushed(PC pc) {
        pc.setState(onPC.getInstance());
    }
 
    @Override
    public void off_button_pushed(PC pc) {
        // 작동하지않음
    }
 
}

// 윈도우 바이오스 진입
public class PcBios implements PCState {
    
    private static PcBios instance = new PcBios();
    
    public static PcBios getInstance() {
        return instance;
    }
 
    @Override
    public void on_buutton_pushed(PC pc) {
        pc.setState(onPC.getInstance());
    }
 
    @Override
    public void off_button_pushed(PC pc) {
        pc.setState(offPC.getInstance());
    }
 
}

 

상태에 필요한 부분들을 인터페이스에 작성하고 상태별로 각자 필요한 메소드들을 Override받아 구현한 결과이다

 

스테이트 패턴을 사용한 이 예시, PC는 현재 상태값을 객체로 가지고있으며 on,off ,f2 button push 의 상태 요청이 왔을때 현재 자신의 상태 객체에게 넘겨주어 진행되도록 한다.

 

조심해야할 점

상태 패턴을 하다보면 전략 패턴과 유사함을 느낄 수 있다.

두 패턴 모두 반복된 조건문들을 추상화하여 중복을 피하는 방법이기 때문이다.

 

하지만 둘은 엄연히 다르고 이를 인지하기위해 간략하게 설명하겠다

 

공통점

두패턴 모두 인터페이스를 사용하여 구현클래스를 캡슐화한다, 그렇기에 해당 Context 클래스에서는 어떤 하위클래스를 할당받는지 모르는 상태로 단순히 Strategy나 State 객체의 추상메소드를 실행한다. 그렇기 때문에 Context 클래스는 별다른 영향을 받지않고 유연하게 수정할 수 있다.

 

 

차이점

전략패턴은 Context 객체 내에 참조 멤버변수를 사용하여 하위클래스에 객체의 참조를 전달하면 Context 객체의 코드변경 없이도 Context의 행동을 변경할 수 있다.

즉, *다형성과 추상메소드 호출을 이용하여 Context 클래스의 코드를 간략하게 할 수 있는 것이다.

-하나의 객체가 여러가지 타입을 가질 수 있는 것, 혹은 하나의 객체가 여러 형태를 받아들일 수 있는 성질

객체의 기능을 확장하거나 변경시 객체 주입만으로 수정이 일어나게 할 수 있다. ex)Override

 

상태 패턴도 추상메소드 호출을 이용한다는 점에서 전략패턴과 같지만 상태 패턴은 외부의 개입없이 상태객체 내부에서 상태에 따라 상태객체를 변경하여 사용한다는점에서 차이가 있다.