일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 비트 코인
- 솔리디티
- Baekjoon
- 알고리즘 문제풀이
- 1546
- 10871
- if문 사용해보기
- 이더리움
- Remix
- 시험 성적
- 그대로 출력하기
- Dapp
- 평균은 넘겠지
- 세 수
- 1065
- 가상 화폐
- 2448
- 10817
- 백준
- 1%d
- 자바스크립트
- for문 사용해보기
- Mist
- X보다 작은 수
- 1110
- 블록 체인
- 더하기 사이클
- 별 찍기 - 11
- 함수 사용하기
- 단계별로 풀어보기
- Today
- Total
블링블링 범블링
상태 패턴(State Pattern) 본문
상태패턴(State Pattern)
상태패턴은 객체의 상태에 따라서 각가 다른 행위를 할 수 있도록 캡슐화한 패턴을 말한다. 동적으로 행동을 교체할 수 있다. 전략패턴과 구조는 같지만 쓰임과 용도가 다르다.
예시를 통해 살펴보자. 커피머신이 있다고 가정하자. 동작 버튼을 눌렀을 때 커피머신의 상태에 따라 다른 동작을 한다. 동작버튼을 눌렀을 때 커피상태가 정상작동이면 커피를 주겠지만, 만약 컵이 없는 경우에는 컵이 없다고 메세지가 뜰 것이다. 그리고 청소하고 있는 상태라면 청소상태를 종료하고 다시 동작상태로 바꿔줘야한다. 이 설명을 아래 코드로 옮겨보았다.
[기존의 코드]
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 | public class CoffeeMachine { public static enum State {EMPTYCUP, CLEAN,GO}; private State state; private int cup; protected int count; public CoffeeMachine() { this .cup = 0 ; this .count= 0 ; state = State.GO; } public void provideCoffee() { System.out.println( "커피만들기" ); } public void decCup() { this .cup--; if ( this .count== 100 ) this .count= 0 ; else this .count++; } public void sendMessage(String comment) { System.out.println(comment); } public void changeState() { if (cup== 0 )state = State.EMPTYCUP; else if (count== 0 )state = State.CLEAN; else state = State.GO; } public void clickStartbutton() { switch (state){ case GO: //작동가능 provideCoffee(); sendMessage( "커피가 나옵니다" ); decCup(); break ; case EMPTYCUP: //컵 부족 sendMessage( "컵이 부족합니다" ); break ; case CLEAN: //청소중 sendMessage( "청소중입니다" ); this .count++; changeState(); break ; } } } |
조금 허술하지만 커피머신이 있다면, 기계의 상태에 따라서 다른 행위를 보여야한다. 하지만 기존의 코드로 계속 구현하면 어떠한 문제가 생길까? 만약 커피재료가 부족한 상태가 추가되면 어떻게 해야할까? case문을 추가해서 커피재룍가 부족할 때 행위를 추가하면 될 것이다. 하지만 전략패턴과 마찬가지로 이렇게 계속해서 상태가 수정되는 경우 중복과 복잡성이 커지게 된다. 그래서 이러한 경우에는 상태패턴을 적용한다.
[상태패턴을 적용한 클래스다이어그램]
커피머신의 각각의 상태를 별도로 분리해서 각 상태에 맞는 행위를 따로 구현한다. 즉, 커피머신이 클라이언트로부터 동작기능의 요청을 받으면 처리를 상태객체에 위임하는 방식이 상태패턴이다.
State 인터페이스를 별도로 만들어서 상태에 따른 행위를 구현해주면된다. Context는 여기서 CoffeeMachine이 되고, Concreate는 각각의 머신의 상태가 된다. 아래는 클래스 다이어그램을 소스코드로 옮긴 것이다.
[상태패턴을 적용한 코드]
[CoffeeMachine Class]
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 | public class CoffeeMachine { private State state; private int cup; protected int count; public CoffeeMachine() { this .cup = 0 ; this .count= 0 ; state = new Go(); } public void provideCoffee() { System.out.println( "커피만들기" ); } public void sendMessage(String comment) { System.out.println(comment); } public void decCup() { this .cup--; if ( this .count== 100 ) this .count = 0 ; else this .count++; } public void changeState() { if (cup== 0 )state = new EmptyCup(); else if (count== 0 )state = new Clean(); else state = new Go(); } public void clickStartbutton() { state.clickStartMachine( this ); state.SendMessage( this ); } } |
[State Interface]
1 2 3 4 | public interface State { public void clickStartMachine(CoffeeMachine cm); public void SendMessage(CoffeeMachine cm); } |
[Go Class]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Go implements State{ @Override public void clickStartMachine(CoffeeMachine cm) { cm.decCup(); cm.provideCoffee(); cm.changeState(); } @Override public void SendMessage(CoffeeMachine cm) { cm.sendMessage( "커피가 나옵니다" ); } } |
[EmptyCup Class]
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class EmptyCup implements State{ @Override public void clickStartMachine(CoffeeMachine cm) { cm.changeState(); } @Override public void SendMessage(CoffeeMachine cm) { cm.sendMessage( "컵이 부족합니다" ); } } |
[Clean Class]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class Clean implements State{ @Override public void clickStartMachine(CoffeeMachine cm) { cm.count++; cm.changeState(); } @Override public void SendMessage(CoffeeMachine cm) { cm.sendMessage( "청소중입니다" ); } } |
상태패턴이 주는 이점은 새로운 상태가 추가되더라도 Context의 변경이 최소화가 된다는 점이다. 상태가 많아질 경우에 복잡성이 커져 유지보수가 어렵게 되지만 상태패턴의 경우에는 상태가 많아져도 코드의 복잡도는 증가하지 않기 때문에 유지보수에 있어서 굉장히 이점이 크다. 그리고 상태에 따른 구현이 각 상태별로 구현하기 때문에 상태의 동작이 변경되더라도 유지보수가 간편해진다. 관련된 코드를 한 곳에 모아 캡슐화하기 때문에 더 안전성이 크고, 변경이 용이하다.
본 코드에서 상태를 변경하는 조건에 컵 개수가 부족하지 않는지, 머신 사용횟수가 100번이되면 청소해야하는 지에 따라 상태를 변경하는데 머신에서 private로 데이터 접근을 하지 못하게 했기 때문에 상태 객체에서 상태를 변경해주는 코드다.
위에 있는 코드에서 상태패턴의 변경은 상태객체에서 이뤄지고 있다. 하지만 상태 객체에서 Context의 상태를 변경하려면 다른 값에 의존해야 하는 경우가 생긴다.
상태변화를 상태 객체에 위임하게 된다면 만약 상태 클래스가 많아지면 상태변경의 규칙을 파악하는데 어려움이 있다. 반대로 상태 종류가 지속적으로 변경되거나 규칙이 바뀌는 경우 Context의 변경처리 코드가 복잡해질 가능성이 있어서 상태의 변경은 상황에 따라서 상태 객체에 위임하거나 Context가 해도된다.
출처 : http://meylady.tistory.com/
'Technology > 객체 지향 디자인 패턴' 카테고리의 다른 글
데코레이터 패턴(Decorator Pattern) (0) | 2018.04.18 |
---|---|
어댑터 패턴(Adapter Pattern) (0) | 2018.04.18 |
템플릿 메서드 패턴(Template Method Pattern) (0) | 2018.04.18 |
전략 패턴(Strategy Pattern) (0) | 2018.04.18 |
객체 지향 프로그래밍(Object-Oriented Programs) (0) | 2018.04.18 |