Java/고급객체지향

스테이트 패턴(State Pattern)

얄루몬 2021. 12. 12. 15:12

스테이트 패턴(State Pattern)

 

 

1. 목적

객체의 내부 상태가 바뀔 때 객체의 동작을 변경할 수 있도록 한다.

객체는 자신의 클래스를 바꾸는 것처럼 보인다.

구분 설명
State(상태) 시점에 따라 특정 상태에 있어야 한다.
처음에 가지게 되는 초기상태(state) 또는 상황에 따라 여러 상태 가운데 한 상태를 가질 수 있다
Transition(전이) 외부 입력에 따라 가능한 상태로 전환

  • 게임 캐릭터: 걷는 상태, 뛰는 상태, 멈춘 상태
  • 가전 제품: on, off, sleep
  • 지하철 개찰구: 열림, 잠금

 

2. 요소

요소 설명
이름 스테이트(State)
문제 상태(state)가 여러 개 있고, if문으로 상태를 통제
해결방안 상태를 한 곳에서 관리
결과  변경 최소화

public class Main {
    public static void main(String[] args) {
        CalcV1 calcv1 = new CalcV1();
        boolean run = true;
        while (run) {
            run = calcv1.run();
        }
    }
}

 

import java.util.Scanner;

public class CalcV1 {
//    private enum STATES { STATE_START, STATE_FIRST_OPERAND, STATE_SECOND_OPERAND, STATE_OPERATOR };
    Scanner scanner;
    String inputStr;
    int operand1; // 첫 번째 피 연산자값 저장
    int operand2; // 두 번째 피 연산자값 저장
//    STATES state;
    State state;
    char operator; // 사칙 연산자 저장
    State stateFirstOperand;
    State stateOperator;
    State stateSecondOperand;
    State stateStart;

    public CalcV1() {
        scanner = new Scanner(System.in);  // Create a Scanner object
//        state = STATES.STATE_START;
        stateFirstOperand = new StateFirstOperand(this);
        stateOperator = new StateOperator(this);
        stateSecondOperand = new StateSecondOperand(this);
        stateStart = new StateStart(this);
        state = stateStart;
    }

    public void setState(State state) {
        this.state = state;
    }

    public void setOperand1(int num1) {
        operand1 = num1;
    }

    public void setOperand2(int num2) {
        operand2 = num2;
    }

    public void setOperator(char op) {
        operator = op;
    }

    public State getFirstOperandState() {
        return stateFirstOperand;
    }

    public State getOperatorState() {
        return stateOperator;
    }

    public State getStartState() {
        return stateStart;
    }

    public State getSecondOperandState() {
        return stateSecondOperand;
    }

    public String getInput(String s) {
        System.out.println(s);
        return scanner.next();
    }

    public void printOutResult() {
        switch (operator) {
            case '+':
                System.out.printf("%d + %d = %d\n", operand1, operand2, operand1 + operand2);
                break;

            case '-':
                System.out.printf("%d - %d = %d\n", operand1, operand2, operand1 - operand2);
                break;

            case '*':
                System.out.printf("%d * %d = %d\n", operand1, operand2, operand1 * operand2);
                break;

            case '/':
                System.out.printf("%d / %d = %d\n", operand1, operand2, operand1 / operand2);
                break;
        }
    }

    /* 정수가 입력되었을 때 처리 */
    public void processNumber(String ch) {
        state.processNumber(Integer.parseInt(ch));
//        if (state == STATES.STATE_START) {
//            operand1 = Integer.parseInt(ch);
//            state = STATES.STATE_FIRST_OPERAND;
//        }
//        else if (state == STATES.STATE_OPERATOR) {
//            operand2 = Integer.parseInt(ch);
//            state = STATES.STATE_SECOND_OPERAND;
//        }
    }

    /* 사칙 연산자 혹은 = 연산자 처리 */
    public void processOperator(char ch) {
        state.processOperator(ch);
//        if (state == STATES.STATE_FIRST_OPERAND) {
//            state = STATES.STATE_OPERATOR;
//            operator = ch;
//        }
//        else if (state == STATES.STATE_SECOND_OPERAND) {
//            if (ch == '=') {
//                printOutResult();
//                state = STATES.STATE_START;
//            }
//        }
    }

    /* 프로그램의 주 로직을 담당하는 함수
       사용자 입력을 기다렸다가 q 또는 Q가 입력되면 종료
       그렇지 않으면 정수가 입력되었는지 혹은 연산자가 입력되었는지에 따라 processNumber 또는 processOperator 함수를 호출
     */
    public boolean run() {
        inputStr = getInput("정수 또는 +,-,*,/,= 기호 중 한 개를 입력하세요");
        char ch = inputStr.charAt(0);
        if (ch == 'q' || ch == 'Q') { // q를 입력하면 프로그램 종료
            return false;
        }
        else if (ch >= '0' && ch <= '9') { // 정수가 입력되면
            processNumber(inputStr);
        }
        else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '=') { // 연산자 처리
            processOperator(ch);
        }
        return true;
    }
}

 

public interface State {
    default void processNumber(int num) {
        System.out.println("processNumber: Invalid operation");
    }

    default void processOperator(char op) {
        System.out.println("processOperator: Invalid operation");
    }
}

 

public class StateFirstOperand implements State {
    CalcV1 calcV1;

    public StateFirstOperand(CalcV1 calcV1) {
        this.calcV1 = calcV1;
    }

    @Override
    public void processOperator(char op) {
        calcV1.setOperator(op);
        calcV1.setState(calcV1.getOperatorState());
    }
}

 

public class StateSecondOperand implements State  {
    CalcV1 calcV1;

    public StateSecondOperand(CalcV1 calcV1) {
        this.calcV1 = calcV1;
    }

    @Override
    public void processOperator(char op) {
        if (op == '=') {
            calcV1.printOutResult();
            calcV1.setState(calcV1.getStartState());
        }
    }
}

 

public class StateOperator implements State {
    CalcV1 calcV1;

    public StateOperator(CalcV1 calcV1) {
        this.calcV1 = calcV1;
    }

    @Override
    public void processNumber(int num) {
        calcV1.setOperand2(num);
        calcV1.setState(calcV1.getSecondOperandState());
    }
}

 

public class StateStart implements State {
    CalcV1 calcV1;

    public StateStart(CalcV1 calcV1) {
        this.calcV1 = calcV1;
    }

    @Override
    public void processNumber(int num) {
        calcV1.setOperand1(num);
        calcV1.setState(calcV1.getFirstOperandState());
    }
}