디자인 패턴 정리 - State Pattern

2021. 11. 19. 17:06·CS/GoF의 Design Pattern 정리

State Pattern

상태(State)란?

하나의 오브젝트가 시점에 따라 특정 상태에 있어야 한다. 처음에 가지게 되는 초기 상태 또는 상황에 따라 여러 상태 중 하나의 상태를 가질 수 있다.

한 상태에서 다른 상태로 전환하는 것을 전이(Transition) 이라고 한다. 예를들어 게임 캐릭터의 경우 걷기, 뛰기, 멈추기, 공격하기, 방어하기 등이 있고, 가전제품의 경우 on, off, sleep 등이 있을 수 있다.

이러한 다양한 상태를 if 문으로 상태를 통제하는 방식은 추가적인 상태가 생기거나 구현의 변경이 발생했을 때 여러곳의 코드가 변경될 수 있다는 문제가 있다.

이를 해결하기 위해 상태를 한 곳에서 관리하기 위한 패턴이 상태 패턴이다.

설명

두개의 인자와 한개의 연산자를 받고 "="을 입력 받으면 계산을 하는 Calculator를 구현한다. 이때 계산기의
상태로는 첫번째 인자 없음, 연산자 없음, 두번째 인자 없음, 계산("=") 인자 없음으로 총 4개의 상태가 존재하게 된다.

또한, 각 상태 별로 숫자 입력, 연산자 입력, 종료 입력의 3가지 전이(Transition)가 발생할 수 있다.

따라서, 모든 상태들을 하나의 상태 인터페이스를 상속하게 하여 각 상태 별로 전이에 대해 적절히 구현하도록 한다.

다이어그램

  • 모든 상태들은 STATE 인터페이스를 implements 하도록 한다.
  • 공통 기능인 quitInput()은 default 메소드로 만들었다.
  • 상태 간 상태가 변경 되는 Transition을 인터페이스에 작성하고 이를 각자 구현한다.

Calculator.class 코드

public class Calculator {
    int operand1; // 첫 번째 피 연산자값 저장
    int operand2; // 두 번째 피 연산자값 저장
    char operator; // 사칙 연산자 저장

    BufferedReader br;
    STATE noOperandOne;
    STATE noOperandTwo;
    STATE noOperator;
    STATE calculate;
    STATE state;

    public Calculator() {
        this.noOperandOne = new noOperandOne(this);
        this.noOperandTwo = new noOperandTwo(this);
        this.noOperator = new noOperator(this);
        this.calculate = new calculate(this);
        this.state = noOperandOne;
        br = new BufferedReader(new InputStreamReader(System.in));
    }

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

    void printOutResult() {
        switch (operator) {
            case '+' -> System.out.printf("%d + %d = %d\n", operand1, operand2, operand1 + operand2);
            case '-' -> System.out.printf("%d - %d = %d\n", operand1, operand2, operand1 - operand2);
            case '*' -> System.out.printf("%d * %d = %d\n", operand1, operand2, operand1 * operand2);
            case '/' -> System.out.printf("%d / %d = %d\n", operand1, operand2, operand1 / operand2);
        }
    }

    boolean run() throws IOException {
        System.out.println("정수 또는 +, -, *, /, = 기호 중 한 개를 입력하세요. (종료 : q)");
        String input = br.readLine();
        char ch;
        if (input.equals("")) {
            ch = '=';
            input = "=";
        } else ch = input.charAt(0);

        if (ch == 'q' || ch == 'Q') {
            state.quitInput();
            return false;
        } else if (ch >= '0' && ch <= '9') {
            state.decimalInput(input);
        } else {
            state.operatorInput(input);
        }
        return true;
    }
}
  • 현재 state를 if문 등을 통해 확인하지 않고 입력 값에 따라 decimalInput(), operatorInput(),
    quitInput()을 수행하도록 한다.
  • STATE 인터페이스를 implements 한 각 상태 구현 코드에서 주어진 전이 값에 따라 적절한 수행을 한다.

STATE interface 코드

public interface STATE {
    void decimalInput(String value);
    void operatorInput(String oper);
    default void quitInput(){
        System.out.println("연산 종료");
    }
}
  • 모든 상태들은 STATE 인터페이스를 상속받아서 관리한다.
  • 상태들은 자신의 현 상태에 따라 전이를 적절히 수행하게 implement 한다.

noOperandOne.class 코드

public class noOperandOne implements STATE {
    Calculator calculator;

    public noOperandOne(Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public void decimalInput(String value) {
        calculator.operand1 = Integer.parseInt(value);
        System.out.println("첫번째 인자 : " + calculator.operand1);
        calculator.setState(calculator.noOperator);
    }

    @Override
    public void operatorInput(String oper) {
        System.out.println("숫자를 입력하세요.");
    }
}
  • 첫번째 인자값을 받아야 하는 상태

noOperator.class 코드

public class noOperator implements STATE{
    Calculator calculator;

    public noOperator (Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public void decimalInput(String value) {
        System.out.println("연산 기호를 입력하세요.");
    }

    @Override
    public void operatorInput(String oper) {
        char tmp = oper.charAt(0);
        if (tmp == '+' || tmp == '-' || tmp == '*' || tmp == '/') {
            calculator.operator = oper.charAt(0);
            System.out.println("연산자 : " + calculator.operator);
            calculator.setState(calculator.noOperandTwo);
        } else {
            System.out.println("잘못된 연산 기호입니다. 다시 입력하세요.");
        }
    }
}
  • 연산기호를 받아야 하는 상태

noOperandTwo.class 코드

public class noOperandTwo implements STATE {
    Calculator calculator;

    public noOperandTwo(Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public void decimalInput(String value) {
        calculator.operand2 = Integer.parseInt(value);
        System.out.println("두번째 인자 : " + calculator.operand2);
        calculator.setState(calculator.calculate);
    }

    @Override
    public void operatorInput(String oper) {
        System.out.println("숫자를 입력하세요.");
    }
}
  • 두번째 인자값을 받아야 하는 상태

calculate.class 코드

public class calculate implements STATE{
    Calculator calculator;

    public calculate (Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public void decimalInput(String value) {
        System.out.println("잘못된 입력입니다. '='를 입력하세요.");
    }

    @Override
    public void operatorInput(String oper) {
        char c = oper.charAt(0);
        if (c == '=') {
            calculator.printOutResult();
            calculator.setState(calculator.noOperandOne);
        } else {
            System.out.println("잘못된 입력입니다. '='를 입력하세요.");
        }
    }
}
  • 결과를 내기 위해 '='를 받아야 하는 상태

결과

  • StatePattern을 사용함으로서 상태애 대해 일일히 if 조건문을 통해 현재 상태를 확인하지 않고도 전이에 대해 수행을 하도록
    할 수 있다.
  • 추가적인 상태가 발생하더라도 코드의 변경을 최소화 할 수 있다.

반응형
저작자표시 비영리 변경금지 (새창열림)

'CS > GoF의 Design Pattern 정리' 카테고리의 다른 글

디자인 패턴 정리 - Template Method Pattern  (0) 2021.11.27
디자인 패턴 정리 - Command Pattern  (0) 2021.11.19
디자인 패턴 정리 - Adapter Pattern, Facade Pattern  (1) 2021.11.19
디자인 패턴 정리 - DAO Pattern Refactoring with Generic  (0) 2021.11.19
디자인 패턴 정리 - DAO Pattern  (0) 2021.11.19
'CS/GoF의 Design Pattern 정리' 카테고리의 다른 글
  • 디자인 패턴 정리 - Template Method Pattern
  • 디자인 패턴 정리 - Command Pattern
  • 디자인 패턴 정리 - Adapter Pattern, Facade Pattern
  • 디자인 패턴 정리 - DAO Pattern Refactoring with Generic
구름뭉치
구름뭉치
구름의 개발일기장
    반응형
  • 구름뭉치
    구름 개발일기장
    구름뭉치
  • 전체
    오늘
    어제
    • ALL (286)
      • 프로젝트 (23)
        • 토스페이먼츠 PG 연동 시리즈 (12)
        • JWT 방식 인증&인가 시리즈 (6)
        • 스우미 웹 애플리케이션 프로젝트 (1)
        • 스프링부트 기본 보일러 플레이트 구축 시리즈 (2)
        • 마이크로서비스 아키텍쳐 시리즈 (1)
      • 스프링 (43)
        • 스프링부트 API 설계 정리 (8)
        • 스프링부트 RestAPI 프로젝트 (18)
        • 스프링부트 WebSocket 적용기 (3)
        • 스프링 JPA 정리 시리즈 (5)
        • 스프링 MVC (5)
        • 스프링 배치 (2)
        • 토비의 스프링 정리 (2)
      • 기술 학습 (2)
        • 아파치 카프카 (9)
        • 클린 코드 (4)
        • 디자인 패턴의 아름다움 (2)
        • 모던 자바 인 액션 (7)
        • JVM 스레드 딥다이브 (7)
      • Web (25)
        • 정리글 (20)
        • GraphQL 정리글 (2)
        • Jenkins 정리글 (3)
      • 취업 (6)
      • CS (77)
        • 네트워크 전공 수업 정리 (11)
        • OSI 7계층 정리 (12)
        • 운영체제 정리 (19)
        • 데이터베이스 정리 (5)
        • MySql 정리 (17)
        • GoF의 Design Pattern 정리 (12)
      • 알고리즘 (70)
        • 백준 (56)
        • 프로그래머스 (12)
        • 알고리즘 정리본 (1)
      • 기초 지식 정리 (2)
      • 일상 (8)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    키보드 손목 받침대
    동유럽
    류블라냐
    레이저
    마우스 패드
    크로아티아
    부다페스트
    마우스
    mx master s3 for mac
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
구름뭉치
디자인 패턴 정리 - State Pattern
상단으로

티스토리툴바