ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java Design Pattern - 스테이트 패턴(State Pattern)
    Language/Java Design Pattern 2022. 11. 20. 19:40

    스테이트 패턴(State Pattern)

    스테이트 패턴이란?

    스테이트 패턴은 객체가 특정 상태에 따라 행위를 달리하는 상황에서, 자신이 직접 상태를 체크하여 상태에 따라 행위를 호출하지 않고, 상태를 객체화하여 상태가 행동을 할 수 있도록 위임하는 패턴을 말한다.

     

     

    즉, 객체의 특정 상태를 클래스로 선언하고, 클래스에서는 해당 상태에서 할 수 있는 행위들을 메서드로 정의한다. 그리고 이러한 각 상태 클래스들을 인터페이스로 캡슐화하여, 클라이언트에서 인터페이스를 호출하는 방식을 말한다.

    여기서 상태란, 객체가 가질 수 있는 어떤 조건이나 상황을 의미한다.

     

    스테이트 패턴의 사용 이유

    예를 들어, 노트북을 켜고 끄는 상황을 생각해보겠다.

     

    1. 노트북 전원이 켜져 있는 상태에서 전원 버튼을 누르면, 전원을 끌 수 있다.

    2. 노트북 전원이 꺼져 있는 상태에서 전원 버튼을 누르면, 전원을 켤 수 있다.

     

    public class Laptop {
        public static String ON = "on";
        public static String OFF = "off";
        private String powerState = "";
        
        public Laptop() {
            setPowerState(Laptop.OFF);
        }
        
        public void setPowerState(String powerState) {
            this.powerState = powerState;
        }
        
        public String powerPush() {
            String result = "";
            if (this.powerState.equals(ON)) {
                result = "전원 off";
            } else if (this.powerState.equals(OFF)) {
                result = "전원 on"
            }
            return result;
        }
    }
    public class Client {
        public static void main(String[] args) {
            Laptop laptop = new Laptop();
            System.out.println(laptop.powerPush());    // "전원 on" 출력
            laptop.setPowerState(Laptop.ON);
            System.out.println(laptop.powerPush());   // "전원 off" 출력
        }
    }

    이런 행위를 할 수 있는 Laptop 클래스는 위 코드와 같이 정의할 수 있다.

    노트북이 on 상태라면 노트북 상태를 off로 변경하고, 상태가 off이면 on으로 변경하는 간단한 코드이다.

     

    간단하게 켜고, 끄는 노트북에 절전모드를 추가한다고 해보겠다. 상태에 따는 동작은 다음과 같다.

     

    1. 노트북 전원이 켜져 있는 상태에서 전원 버튼을 누르면, 전원을 끌 수 있다.

    2. 노트북 전원이 꺼져 있는 상태에서 전원 버튼을 누르면, 절전모드가 된다.

    3. 노트북 절전모드 상태에서 전원 버튼을 누르면, 전원을 켤 수 있다.

     

    public class Laptop {
        public static String ON = "on";
        public static String OFF = "off";
        public static String SAVING = "saving";
        private String powerState = "";
        
        public Laptop() {
            setPowerState(Laptop.OFF);
        }
        
        public void setPowerState(String powerState) {
            this.powerState = powerState;
        }
        
        public String powerPush() {
            if (this.powerState.equals(ON)) {
                return "전원 off";
            } else if (this.powerState.equals(SAVING)) {
                return "전원 on"
            } else if (this.powerState.equals(OFF)) {
                return "절전 모드"
            }
        }
    }
    public class Client {
        public static void main(String[] args) {
            Laptop laptop = new Laptop();
            System.out.println(laptop.powerPush());    // "절전 모드" 출력
            laptop.setPowerState(Laptop.ON);
            System.out.println(laptop.powerPush());   // "전원 off" 출력
            laptop.setPowerState(Laptop.SAVING);
            System.out.println(laptop.powerPush());   // "전원 on" 출력
        }
    }

    절전모드가 추가된 Laptop 클래스는 다음과 같이 조건문이 하나 더 추가된다.

     

    위와 같이 조건문이 하나 추가된다고 해서 크게 불편한 것은 없다. 하지만, 상태가 여러 개 있다면 코드는 굉장히 길어질 것이기 때문에, 상태에 따라 하고자 하는 행위를 파악하기가 쉽지는 않을 것이다.

     

    또한, Laptop 클래스의 powerPush() 메서드뿐만 아니라, TV의 powerPush(), SmartPhone의 powerPush() 메서드에서 동일한 분기문이 사용된다면, 기능이 변경될 때마다 일일이 다 찾아가서 수정을 해야 한다.

     

    이렇게, 상태에 따라 행위를 달리해야 하는 경우에 사용하는 패턴이 스테이트 패턴이다.

     

    스테이트 패턴의 적용

     

    스테이트 패턴을 적용하면 ON, OFF, SAVING과 같은 각 상태를 클래스로 정의하고, 이들을 하나의 인터페이스로 묶는다.

    그러고 나서 Laptop 클래스가 상태 인터페이스의 메서드를 호출하면, 각 상태 클래스에서 정의된 행위가 수행되는 방식이다.

     

    public interface PowerState {
        public String powerPush();
    }

    우선, 전원 상태를 캡슐화한 인터페이스를 선언하다.

     

    public class On implements PowerState {
        @Override
        pulbic String powerPush() {
            return "전원 off";
        }
    }
    public class Off implements PowerState {
        @Override
        public String powerPush() {
            return "절전 모드";
        }
    }
    public class Saving implements PowerState{
        @Override
        public String powerPush() {
            return "전원 on";
        }
    }

    다음으로 PoswerState 인터페이스를 구현한 각 상태 클래스를 정의한다.

     

    public class Laptop {
        private PowerState powerState;
        
        public Laptop {
            this.powerState = new Off();
        }
        
        public void setPowerState(PowerState powerState) {
            this.powerState = powerState;
        }
        
        public String powerPush() {
            this.powerState.powerPush();
        }
    
    }

    이어서 Laptop 클래스를 수정한다.

    Laptop 클래스는 이제 상태를 분기하는 코드가 사라지고, 인터페이스의 powerPush() 메서드를 호출하기만 하면 된다.

     

    public class Client {
        public static void main(String[] args) {
            Laptop laptop = new Laptop();
            On on = new On();
            Off off = new Off();
            Saving saving = new Saving();
            
            System.out.println(laptop.powerPush());   // "절전 모드" 출력
            laptop.setPowerState(on);
            System.out.println(laptop.powerPush());   // "전원 off" 출력
            laptop.setPowerState(saving);
            System.out.println(laptop.powerPush());   // "전원 on" 출력
        }
    }

    마지막으로, Laptop 객체를 사용하는 Client 클래스를 정의한다. 이전과 결과는 동일하다.

     

    이상으로 스테이트 패턴에 대해서 알아보았다. 스테이트 패턴의 구현 과정을 보면, 전략 패턴과 상당히 유사하다는 것을 알 수 있다.

    굳이 사용을 구분하자면, 전략 패턴은 상속을 대체하려는 목적으로, 스테이트 패턴을 코드 내의 조건문들을 대체하려는 목적으로 사용된다.


    출처

    https://victorydntmd.tistory.com/294

     

    728x90

    댓글

Designed by Tistory.