해당 포스팅은 한빛 미디어 헤드퍼스트 디자인 패턴(에릭 프리먼, 엘리자베스 롭슨 저)를 통해 공부한 내용을 정리한 블로그입니다.

아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
좋은 프로그래머는 자기 두뇌를 사용한다. 그러나 좋은 가이드라인은 모든 케이스를 고려해야만 하는 노력을 줄여준다.
(Francis Glassborow, 개발자)

 

전략 패턴과 상태 패턴은 쌍둥이

  • 상태 다이어그램과 비슷
  • 다른 상태로 전환을 위해서는 어떤 동작이 필요
  • 예외 케이스에 대해서도 항상 잘 생각해야함.
  • 상태 값을 저장하는 인스턴스 변수를 만들고 메소드 내에서 조건문을 써서 다양한 상태를 처리

State Diagram

상태

  • 동전 없음
  • 동전 있음
  • 알맹이 없음
  • 알맹이 판매

상태를 저장하는 인스턴스 변수 정의

/*    
* 알맹이 상태 저장
*/   
final static int SOLD_OUT = 0; // 알맹이 없음
final static int NO_QUARTER = 1; // 동전 없음   
final static int HAS_QUARTER = 2; // 동전 있음   
final static int SOLD = 3; // 알맹이 내보내는 중

시스템에서 일어날 수 있는 모든 행동 정의

  • 동전투입
  • 동전 반환
  • 손잡이 돌림
  • 알맹이 내보냄

클래스 만들기

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 { // state == SOLD
            System.out.println("알맹이를 내보내는 중");
        }
    }

	// 이후 부터는 행동 정의 생략...
    
    /*
     * 동전 반환
     */
    public void ejectQuarter() {
    }

    /*
     * 손잡이를 돌리는 경우
     */
    public void turnCrank() {
    }

    /*
     * 알맹이 내보내기
     */
    public void dispense() {
    }

    public void refill(int numGumBalls) {
    }
}

뽑기 기계 수정 요청


  • 새로운 기능 추가 요구
  • 현재 코드 ⇒ 관리 및 수정의 어려움
  • 상태 별 행동을 별도의 클래스에 넣어두고 모든 상태에 대해 각각 자기가 할 일을 구현
    • 바뀌는 부분을 캡슐화
    • 뽑기 기계가 현재 상태를 나타내는 상태 객체에게 작업을 넘기도록 ⇒ 구성

새로운 디자인 구성하기

  • 기존 코드를 그대로 활용하는 대신 상태 객체들을 별도의 코드에 넣고 어떤 행동이 일어나면 현재 상태 객체에서 필요한 작업을 처리.
    • 뽑기 기계와 관련된 모든 행동에 관한 메소드가 들어있는 State 인터페이스 정의
    • 기계의 모든 상태를 대상으로 상태 클래스 구현.
      • 상태에 대해서는 상태 클래스가 모든 책임을 갖도록
    • 조건문 코드 없애고 상태 클래스에 작업 위임.
  • 상태 패턴 적용

State Pattern

구조 살펴보기

  • 해당 결과 도출
    • 각 상태의 행동을 별개의 클래스로 국지화
    • 관리하기 힘든 분기문 삭제
    • 상태를 변경에는 닫혀있고 새로운 상태를 추가하는 확장에는 열려있도록. (OCP적용)
    • 이해하기 좋은 코드 베이스와 클래스 구조 적용
public class GumballMachine {

    /*
     * 알맹이 상태 저장
     */
    State soldOutState; // 알맹이 없음
    State noQuarterState; // 동전 없음
    State hasQuarterState; // 동전 있음
    State soldState; // 알맹이 내보내는 중

    State state;
    int count = 0;

    public GumballMachine(int count) {
        soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);

        this.count = count;
        if (count > 0) {
            state = noQuarterState;
        } else {
            state = soldOutState;
        }
    }

    /*
     * 동전이 투입된 경우
     */
    public void insertQuarter() {
        state.insertQuarter();
    }

    /*
     * 동전 반환
     */
    public void ejectQuarter() {
        state.ejectQuarter();
    }

    /*
     * 손잡이를 돌리는 경우
     */
    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }

	// State 생성
    void setState(State state) {
        this.state = state;
    }
}

// State 예시
public interface State {
    // 동전을 넣었을 때의 상태 정의
    public void insertQuarter();

    // 동전을 반환했을 때의 상태 정의
    public void ejectQuarter();

    // 손잡이를 돌렸을 때의 상태 정의
    public void turnCrank();

    // 알맹이가 나올 때의 상태 정의
    public void dispense();

    public void refill();
}

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 turnCrank() {
        System.out.println("손잡이를 돌리셨습니다.");
        gumballMachine.setState(gumballMachine.getSoldState());
    }

    @Override
    public void dispense() {
        System.out.println("동전을 넣어주세요");
    }

    public void refill() {}
}

State Pattern

State Pattern

💡 상태 패턴
객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있음. 클래스가 바뀌는 것과 같은 결과
  • 상태를 별도의 클래스로 캡슐화
  • 상태 패턴과 전략 패턴은 비슷!!
    • 하지만 용도가 다름
    • 상태 패턴
      • 상태 객체에 일련의 행동이 캡슐화
      • 여러 상태 객체 중 하나의 상태에 모든 행동을 맡기게 됨.
      • 그에 따라 객체가 바뀌게 될 수도 있음
    • 전략 패턴
      • 클라이언트가 context 객체에게 어떤 전략을 사용할 지를 지정
      • 실행 시에 전략 객체를 변경할 수 있는 유연성을 제공하는 용도로 사용.
      • 서브클래스를 만드는 방법을 대신해 유연성 극대화 ⇒ 구성

+ Recent posts