본문 바로가기
CS

스테이트 패턴

by 너츠너츠 2022. 7. 22.

선풍기나 형광등의 경우 전원을 키거나 끌 수 있다. 켜져있거나 꺼져 있는 "상태"에 대한 처리가 스테이트 패턴의 핵심이다.

 

상태란 객체가 시스템에 존재하는 동안, 즉 객체의 라이프 타임 동안 객체가 가질 수 있는 어떤 조건이나 상황을 표현한다.

ex) 어떤 액티비티 등을 수행하거나 특정 이벤트가 발생하기를 기다리는 것이다.

 

상태 진입은 객체의 한 상태에서 다른 상태로 이동하는 것을 말한다. 

ex) 선풍기가 전원이 켜진 상태로 진입

 

형광등을 키고 끄는 과정을 하나의 객체로서 코드로 표현하자면 아래와 같습니다

public class Light {
    private static int ON = 0;
    private static int OFF = 1;
    private int state;
    
    public Light() {
        state = OFF;
    }
    
    public void on() {
        if (state == ON) {
            System.out.println("반응 없음");
        } else {
            System.out.println("Light On");
            state = ON;
        }
    }
    
    public void off() {
        if (state == OFF) {
            System.out.println("반응 없음");
        } else {
            System.out.println("Light Off");
            state = OFF;
        }
    }
}

 

이 코드의 문제점은 취침등를 구현해달라는 요구사항이 추가된다면 코드를 확장시켜야한다.

public class Light {
    private static int ON = 0;
    private static int OFF = 1;
    private static int SLEEPING = 2;
    private int state;

    public Light() {
        state = OFF;
    }

    public void on() {
        if (state == ON) { // On 상태에서 한번 더 누르면 취침등 상태로 전환
            System.out.println("반응 없음"); 
        } else if (state == SLEEPING) {
            System.out.println("Light On");
            state = ON;
        } else {
            System.out.println("Light On");
            state = ON;
        }
    }

    public void off() {
        if (state == OFF) {
            System.out.println("반응 없음");
        } else if (state == SLEEPING) {
            System.out.println("Light Off");
            state = OFF;
        } else {
            System.out.println("Light Off");
            state = OFF;
        }
    }
}

 

이런 경우 OCP를 만족하지 않게 되는데 이를 해결하기 위해서 스테이트 패턴을 적용하는 것이다.

스테이트 패턴은 스트래티지 패턴과 매우 흡사하다. 

 

State 인터페이스 및 각 상태에 대한 클래스를 구현하여 살펴보자.

interface State {
   public void on(Light light);
   public void off(Light light);
}
public class ON implements State {
   public void on(Light light) {
      System.out.println("반응 없음");
   }
   public void off(Light light) {
      System.out.println("Light Off");
      light.setState(new OFF(light));
   }
}
public class OFF implements State {
   public void on(Light light) {
      System.out.println("Light ON");
      light.setState(new ON(light));
   }
   public void off(Light light) {
      System.out.println("반응 없음");
   }
}
public class Light {
   private State state;
   
   public Light() {
      state = new OFF();
   }
   
   public void setState(State state) {
      this.state = state;
   }
   
   public void on() {
      state.on();
   }
   
   public void off() {
      state.off();
   }
}

이렇게 처리할 경우 Light 객체에서는 State를 변경해주기만 하면 쉽게 그 State를 상속받은 상태들에 대해서 작동할 수 있지만 상태를 바꿀 때마다 객체가 생성된다는 단점이 있다.

 

이 부분을 싱글톤을 적용해 해결해보자

public class ON implements State {
   private static ON on = new ON();
   private ON() { }
   
   public static ON getInstance() {
      return on;
   }
   
   public void on(Light light) {
      System.out.println("반응 없음");
   }
   
   public void off(Light light) {
      light.setState(OFF.getInstance());
      System.out.println("Light Off");
   }
}
public class OFF implements State {
   private static OFF off = new OFF();
   private OFF() { }
   
   public static OFF getInstance() {
      return off;
   }
   
   public void on(Light light) {
      light.setState(ON.getInstance());
      System.out.println("Light On");
   }
   
   public void off(Light light) {
      System.out.println("반응 없음");
   }
}

 

반응형

'CS' 카테고리의 다른 글

옵저버 패턴 (Observer Pattern)  (0) 2022.08.02
커맨드 패턴 (Command Pattern)  (0) 2022.07.27
싱글톤 패턴 (Singleton Pattern)  (0) 2022.07.18
스트래티지 패턴 (Strategy Pattern)  (0) 2022.07.06
객체지향 원리  (0) 2022.06.24

댓글