디자인 패턴 - 스테이트 패턴

안성은·2022년 5월 11일
0

Disign Pattern

목록 보기
9/9

스테이트 패턴

정의

  • 객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있다. 내부적으로 객체의 클래스가 바뀌는 것과 같은 결과를 얻는다.

사용하는 이유 및 특징

  • 조건문을 사용해서 상태를 관리하는 것은 코드를 복잡하게 만들어 유지 보수를 어렵게한다.
  • 동일한 메서드가 상태에 따라서 다르게 동작할 때 사용할 수 있는 패턴으로 상태를 별도의 타입으로 분리하고 알맞은 하위 타입을 구성하는 것이 특징이다.
  • 상태가 많아질 수록 관리해야하는 클래스가 많아진다는 단점이 존재한다.

UML

  • UML

  • 실제 적용 UML
    - 공통 STATE 인터페이스를 사용하고 모든 상태를 캡슐화를 통해서 관리한다.

개선 과정 및 코드

  • LEGACY CODE
public class GumballMachine {
    final static int SOLD_OUT = 0; // 매진
    final static int NO_QUARTER = 1; // 동전 없음
    final static int HAS_QUARTER = 2; // 동전 있음
    final static int SOLD = 3; //판매

    int state = SOLD_OUT;
    int count = 0;

    public GumballMachine(int count) {
        this.count = count;
        if (count > 0) {
            state = NO_QUARTER;
        }
    }

    //동전 삽입
    public void insertQuarter() {
        if (state == HAS_QUARTER) {
            System.out.println("동전은 한 개만 넣어주세요.");
        } else if (state == NO_QUARTER) {
            state = HAS_QUARTER;
            System.out.println("동전을 넣으셨습니다.");
        } else if (state == SOLD_OUT) {
            System.out.println("매진되었습니다. 다음 기회에 이용해주세요.");
        } else if (state == SOLD) {
            System.out.println("잠깐만 기다려 주세요. 알멩이가 나가고 있습니다.");
        }
    }

    //동전 반환
    public void ejectQuarter() {
        if (state == HAS_QUARTER) {
            System.out.println("동전이 반환됩니다.");
            state = NO_QUARTER;
        } else if (state == NO_QUARTER) {
            System.out.println("동전을 넣어주세요.");
        } else if (state == SOLD) {
            System.out.println("이미 알멩이를 뽑으셨습니다.");
        } else if (state == SOLD_OUT) {
            System.out.println("동전을 넣지 않으셨습니다. 동전이 반환되지 않습니다.");

        }
    }

    //손잡이 돌리기
    public void turnCrank() {
        if (state == SOLD) {
            System.out.println("손잡이는 한 번만 돌려주세요.");
        } else if (state == NO_QUARTER) {
            System.out.println("동전을 넣어주세요");
        } else if (state == SOLD_OUT) {
            System.out.println("매진되었습니다");
        } else if (state == HAS_QUARTER) {
            System.out.println("손잡이를 돌리셨습니다.");
            state = SOLD;
            dispense();
        }
    }

	//방출
    public void dispense() {
        if (state == SOLD) {
            System.out.println("알멩이가 나가고 있습니다.");
            count = count - 1;
            if (count == 0) {
                System.out.println("더 이상 알멩이가 없습니다.");
                state = SOLD_OUT;
            } else {
                state = NO_QUARTER;
            }
        } else if (state == NO_QUARTER) {
            System.out.println("동전을 넣어주세요");
        } else if (state == SOLD_OUT) {
            System.out.println("매진입니다");
        } else if (state == HAS_QUARTER) {
            System.out.println("알멩이가 나갈 수 없습니다.");
        }
    }

    @Override
    public String toString() {
        return "GumballMachine{" +
                "state=" + state +
                ", count=" + count +
                '}';
    }
}
  • 개선 코드

    • 디렉토리 구조

      • 상수로 관리되던 상태 값들을 상속 및 클래스화
    • GumballMachine

      public class GumballMachine {
        State soldOutState;
        State noQuarterState;
        State hasQuarterState;
        State soldState;
        State winnerState;
      
        State state = soldOutState;
        int count = 0;
      
        public GumballMachine(int numberGumballs) {
            soldOutState = new SoldOutState(this);
            noQuarterState = new NoQuarterState(this);
            hasQuarterState = new HasQuarterState(this);
            soldState = new SoldState(this);
            this.count = numberGumballs;
            if ( numberGumballs > 0 ) {
                state = noQuarterState;
            }
        }
      
        public void insertQuarter() {
           state.insertQuarter();
        }
      
        public void ejectQuarter() {
            state.ejectQuarter();
        }
      
        public void tumCrank() {
            state.tumCrank();
        }
      
        public void dispense() {
            state.dispense();
        }
      
        void releaseBall() {
            System.out.println("A gumball comes rolling out the slot...");
            if (count != 0) {
                count = count - 1;
            }
        }
      }
  • State Interface

    public interface State {
      void insertQuarter();
      void ejectQuarter();
      void tumCrank();
      void dispense();
    }
    
  • HasQuarterState

    public class HasQuarterState implements State{
    
      GumballMachine gumballMachine;
    
      public HasQuarterState(GumballMachine gumballMachine) {
          this.gumballMachine = gumballMachine;
      }
    
      @Override
      public void insertQuarter() {
          System.out.println("동전은 한 개만 넣어주세요.");
      }
    
      @Override
      public void ejectQuarter() {
          System.out.println("동전이 반환됩니다");
          gumballMachine.setState(gumballMachine.getNoQuarterState());
      }
    
      @Override
      public void tumCrank() {
          System.out.println("손잡이를 돌리셨습니다");
          gumballMachine.setState(gumballMachine.getSoldState());
      }
    
      @Override
      public void dispense() {
          System.out.println("알멩이가 나갈 수 없습니다.");
    
      }
  • NoQuarterState

    public class NoQuarterState implements State{
      GumballMachine gumballMachine;
    
      public NoQuarterState(GumballMachine gumballMachine) {
          this.gumballMachine = gumballMachine;
      }
    
      @Override
      public void insertQuarter() {
          System.out.println("동전을 넣으셨습니다.");
          gumballMachine.setState(gumballMachine.getHasQuarterState());
      }
    
      @Override
      public void ejectQuarter() {
          System.out.println("동전을 넣어주세요");
      }
    
      @Override
      public void tumCrank() {
          System.out.println("동전을 넣어주세요");
      }
    
      @Override
      public void dispense() {
          System.out.println("동전을 넣어주세요");
      }
    }
  • SoldOutState

    public class SoldOutState implements State{
    
      GumballMachine gumballMachine;
    
      public SoldOutState(GumballMachine gumballMachine) {
          this.gumballMachine = gumballMachine;
      }
    
      @Override
      public void insertQuarter() {
          System.out.println("동전을 넣으셨습니다.");
          gumballMachine.setState(gumballMachine.getHasQuarterState());
      }
    
      @Override
      public void ejectQuarter() {
          System.out.println("동전을 넣어주세요");
      }
    
      @Override
      public void tumCrank() {
          System.out.println("동전을 넣어주세요");
      }
    
      @Override
      public void dispense() {
          System.out.println("동전을 넣어주세요");
      }
    }
  • SoldState

    public class SoldState implements State{
    
      GumballMachine gumballMachine;
    
      public SoldState(GumballMachine gumballMachine) {
          this.gumballMachine = gumballMachine;
      }
    
      @Override
      public void insertQuarter() {
          System.out.println("잠시만 기다려주세요. 지금 뽑기가 진행중 입니다");
      }
    
      @Override
      public void ejectQuarter() {
          System.out.println("이미 알멩이를 뽑으셨습니다");
      }
    
      @Override
      public void tumCrank() {
          System.out.println("손잡이는 한 번만 돌려주세요");
      }
    
      @Override
      public void dispense() {
          gumballMachine.releaseBall();
          if (gumballMachine.getCount() > 0) {
              gumballMachine.setState(gumballMachine.getNoQuarterState());
          } else {
              System.out.println("Oops, out of gumballs");
              gumballMachine.setState(gumballMachine.getSoldOutState());
          }
      }
    }
  • GumballMachineTest

    public class GumballMachineTest {
      public static void main(String[] args) {
          GumballMachine gumballMachine = new GumballMachine(5);
    
          System.out.println(gumballMachine);
    
          gumballMachine.insertQuarter();
          gumballMachine.tumCrank();
    
          System.out.println(gumballMachine);
    
          gumballMachine.insertQuarter();
          gumballMachine.tumCrank();
          gumballMachine.insertQuarter();
          gumballMachine.tumCrank();
    
          System.out.println(gumballMachine);
    
          gumballMachine.releaseBall();
    
          System.out.println(gumballMachine);
      }
    }

0개의 댓글