Design Pattern

[Design Patter] 상태패턴

JiWonSon 2023. 1. 7. 20:22

1. 상태패턴이란?

상태를 객체화하여 필요에 따라 다르게 행동하도록 위임하는 디자인 패턴이다.

즉, 객체의 내부 상태가 바뀜에 따라 객체의 행동을 바꿀 수 있다.

  • 객체의 특정 상태 = 클래스
  • 상태에 따른 행위 = 클래스 내의 메서드
  • ex) 뽑기 기계를 코드로 구현할 때 각각 알맹이 매진, 동전 없음, 동전 있음, 알맹이 판매 4가지의 상태가 있다.
    4가지의 상태 클래스를 인터페이스로 캡슐화 시킨다.

 

2. 상태패턴을 사용하는 이유

뽑기 예제를 예시로 들어보자.

아래 코드와 같이 구현하면, 새로운 추가 요청 사항이 생긴다면 모든 메서드에 조건문을 추가해줘야 한다.

따라서 상태패턴을 "확장성"을 더욱 용이하기 위해 사용한다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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("no more gum ball");
                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("알맹이가 나갈 수 없습니다.");
        }
    }
    //toString(), refill( ) etc
    
}
cs

 

 

3. 상태패턴을 적용 후

3-1. state 인터페이스

1
2
3
4
5
6
7
public interface State {
        public void insertQuarter();
        public void ejectQuarter();
        public void turnCrank();
        public void dispense();
 }
cs


3-2. NoQuarterState 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class NoQuarterState implements State {
        GumballMachine gumballMachine;
    
        public NoQuarterState(GumballMachine gumballMachine) {
            this.gumballMachine = gumballMachine;
        }
    
        public void insertQuarter() {
            System.out.println("동전을 넣었습니다.");
            gumballMachine.setState(gumballMachine.getHasQuarterState()); 
        }
    
        public void ejectQuarter() {
            System.out.println("동전을 넣지 않았습니다.");
        }
    
        public void turnCrank() {
            System.out.println("손잡이를 돌렸지만, 동전이 없습니다.");
        }
    
        public void dispense() {
            System.out.println("손잡이를 돌리기 전에, 동전을 넣어주세요.");
        }
    }
cs

 

3-3. GumballMachine 클래스

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 public class GumballMachine {
 
        //클래스로 정의한 상태들
        State soldOutState;
        State noQuarterState;
        State hasQuarterState;
        State soldState;
    
        State state;  
        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;
            } else {
                state = soldOutState;
            }
        }
    
        public void insertQuarter() {
            state.insertQuarter();
        }
    
        public void ejectQuarter() {
            state.ejectQuarter();
        }
 
        public void turnCrank() {
            state.turnCrank();
            state.dispense();  
        }
 
    
        int getCount() {
            return count;
        }
 
        void setState(State state) {
            this.state = state;
        }
    
        public State getState() {
            return state;
        }
    
        public State getSoldOutState() {
            return soldOutState;
        }
    
        public State getNoQuarterState() {
            return noQuarterState;
        }
    
        public State getHasQuarterState() {
            return hasQuarterState;
        }
    
        public State getSoldState() {
            return soldState;
        }
    
    }
cs

 

3-4. WinnerState.java(당첨기능 추가하기)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class WinnerState implements State {
    GumballMachine gumballMachine;
 
    public WinnerState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
 
    public void insertQuarter() {
        System.out.println("알맹이를 내보내고 있습니다.");
    }
 
    public void ejectQuarter() {
        System.out.println("알맹이를 내보내고 있습니다.");
    }
 
    public void turnCrank() {
        System.out.println("손잡이는 한 번만 돌려주세요.");
    }
 
    public void dispense() {
        System.out.println("축하드립니다. 알맹이를 하나 더 받으실 수 있습니다.");
        gumballMachine.releaseBall();
        if (gumballMachine.getCount() == 0) {
            gumballMachine.setState(gumballMachine.getSoldOutState());
        } else {
            gumballMachine.releaseBall();
            if (gumballMachine.getCount() > 0) {
                gumballMachine.setState(gumballMachine.getNoQuarterState());
            } else {
                System.out.println("더 이상 알맹이가 없습니다.");
                gumballMachine.setState(gumballMachine.getSoldOutState());
            }
        }
    }
 
    public String toString() {
        return "despensing two gumballs for your quarter, because YOU'RE A WINNER!";
    }
}
cs

'Design Pattern' 카테고리의 다른 글

[Design Pattern] 템플릿 메소드 패턴  (0) 2023.03.05
[Design Pttern] 데코레이터 패턴  (2) 2023.02.05
[Design Pattern] MVC패턴  (0) 2023.01.09
[Design Pattern] 싱글톤 패턴  (0) 2022.12.17