SCHOOL/고급객체지향 프로그래밍

[State Pattern] 실습 과제 #9

chaerrii 2023. 12. 6. 19:26

실습 과제 9를 하는데 내가 푼 과제 분석을 하기 위해 글을 작성한다.

State Machine 의 구조(?)

구조는 이렇다. 

계산기를 만든다. Operand1은 피연산자, Operator은 연산자, Operand2는 두번째 피연산자이다. 각각을 '상태' 로 변형했다.

처음에는 시작 상태이다. 숫자를 입력받으면 operand1 상태로 넘어가고, 계속해서 숫자를 한 글자씩 입력받는 형태이기 때문에 숫자만 입력하면 계속 Operand1 형태이다. 이런식으로 이어가다가 +, -, \, * 같은 연산자를 입력받으면 Operator 상태로 넘어가며, 여기서 숫자를 입력받으면 Operand2, 연산자를 입력받으면 덮어씌운다. 이렇게 하다가 =을 입력 받으면 피연산자들끼리 계산하는 형식이다.

Main

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Calculator c = new Calculator();
        char ch = sc.next().charAt(0);      // 키보드에서 한 글자 입력 받기
        while (ch != 'q' && ch != 'Q') {// 종료 문자가 아니면 반복
            if(ch == '='){
                c.processEqualOperator();
            }
            else if(ch == '+' || ch == '-' || ch == '/' || ch == '*'){
                c.processArithmeticOperator(ch);
            }
            else{
                c.processDigit(ch - '0');
            }
            ch = sc.next().charAt(0);
        }
    }
}

 

처음에 글자를 입력 받은 다음에 종료 버튼인 q가 입력받기 전까지는 계속 반복한다.

sc.next().charAt(0);을 이용해서 한글자씩 입력받는다.

Caculator (context)

public class Calculator {
    State state; // 현재 context의 상태
    private String op1; //피연산자 1 (숫자를 계속 이어붙일 것이기 때문에 String 형태로 변환한다.)
    private String op2; // 피연산자 2 (마찬가지이다.)
    private char ope; // 연산자
    public Calculator(){
        initialize();
        state = new StartState(this); //처음에 불려졌을 때는 시작 상태이다.
    }
    public void initialize(){ //초기화
        this.op1 = "";
        this.op2 = "";
    }
    public void caculate(){
        if(this.ope=='+'){
            int result = ((Integer.parseInt(op1)) + (Integer.parseInt(op2)));
            System.out.println("결과 값: " + (Integer.parseInt(op1)) +ope+(Integer.parseInt(op2))+" = "+result);
        }
        if(this.ope=='-'){
            int result = ((Integer.parseInt(op1)) - (Integer.parseInt(op2)));
            System.out.println("결과 값: " + (Integer.parseInt(op1)) +ope+(Integer.parseInt(op2))+" = "+result);
        }
        if(this.ope=='*'){
            int result = ((Integer.parseInt(op1)) * (Integer.parseInt(op2)));
            System.out.println("결과 값: " + (Integer.parseInt(op1)) +ope+(Integer.parseInt(op2))+" = "+result);
        }
        if(this.ope=='/'){
            int result = ((Integer.parseInt(op1)) / (Integer.parseInt(op2)));
            System.out.println("결과 값: " + (Integer.parseInt(op1)) +ope+(Integer.parseInt(op2))+" = "+result);
        }
    }
    public void processDigit(int digit) {
        state.processDigit(digit);
    }
    public void processArithmeticOperator(char ch) {
       state.processArithmeticOperator(ch);
    }

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

    public State getState() {
        return state;
    }

    public String getOp1() {
        return op1;
    }

    public void setOp1(int op1) {
        this.op1 += op1;
    }

    public String getOp2() {
        return op2;
    }

    public void setOp2(int op2) {
        this.op2 += op2;
    }

    public char getOpe() {
        return ope;
    }

    public void setOpe(char ope) {
        this.ope = ope;
    }
}

한글자씩 숫자(char - 0 으로 변환) 을 입력 받으므로, 일단 String으로 해두고 계속 + 해준다. (int 형이면 더해버리기 때문에)

의문이었던 점은 교수님이 코드에서 각각 State들을 다 멤버변수로 지정하셨는데 이해가 안간다 왜지 ?

우테코 풀면서 객체 참조전달값에 엄청..엄청 고생을 했기 때문에 혹시 그거 때문에 값이 꼬이나? 이생각했다. 그치만 어차피 State에서 상태를 바꾸면 되지 않나(...) 라고 생각했다 😩 내가 뭐라는지 나도 모르겠음ㅋㅋ

 

Concrete State 들

State를 구현한 Concrete State 

public class StartState implements State {
    private Calculator calculator;
    public StartState(Calculator calculator){
        this.calculator = calculator;
    }

    @Override
    public void processDigit(int digit) {
        calculator.setOp1(digit);
        calculator.changeNextState(new Operand1(calculator));
    }
}
public class Operand1 implements State {
    private Calculator calculator;
    public Operand1(Calculator calculator){
        this.calculator = calculator;
    }

    @Override
    public void processDigit(int digit) {
        calculator.setOp1(digit);
    }
    public void processArithmeticOperator(char ch) {
        calculator.setOpe(ch);
        calculator.changeNextState(new Operator(calculator));
    }
}
public class Operator implements State {
    private Calculator calculator;
    public Operator(Calculator calculator){
        this.calculator = calculator;
    }

    @Override
    public void processDigit(int digit) {
        calculator.setOp2(digit);
        calculator.changeNextState(new Operand2(calculator));
    }
    public void processArithmeticOperator(char ch) {
        calculator.setOpe(ch);
    }
}
public class Operand2 implements State {
    private Calculator calculator;
    public Operand2(Calculator calculator){
        this.calculator = calculator;
    }
    @Override
    public void processDigit(int digit) {
        calculator.setOp2(digit);
    }
    public void processEqualOperator() {
        calculator.caculate();
        calculator.changeNextState(new StartState(calculator));
        calculator.initialize();
    }
}

리팩토링?

나중에 기회가 되면 하겠지만 new State() 처럼 새로 객체를 생성하는 부분을 싱글톤 패턴으로 바꿔보는 것이다. 시험 전에 언젠간 하겠지? 

그리고 우테코 미션도 뭔가 state 패턴으로 바꿔볼 수 있겠다는 생각을 했다. 여러 객체가 링크되어 있어서 고생했던 기억이 나는데...수업에서 배웠던 디자인 패턴을 적용하면 좋을 거 같다.