본문 바로가기

Design Pattern

State Pattern (스테이트 패턴)

 State Pattern(스테이트 패턴)이란, 일련의 규칙에 따라 객체의 상태(State)를 변화시켜, 객체가 할 수 있는 행위를 바꾸는 패턴이다.

 상태에 따라 행동이 변화하는 객체엔 모두 적용할 수 있다. 이번 기회에 스테이트 패턴을 적용하여 알람 시계를 한 번 만들어 보도록 하자. 기능은 단순하게 normal, alarm상태 두 가지로 한정한다. State 패턴의 핵심은 상태를 인터페이스로 분리시키는 것이다. 따라서 상태들을 유연하게 관리할 수 있기 때문에 유지 보수에 효율적이다.

  우선 시계의 상태를 인터페이스로 정의해보자. 시계는 normal(보통일 때의 상태), 와 alarm(알람이 울릴 때 상태) 두가지 경우를 가지고 있다.

 

 우리가 만들고자하는 Clock 클래스를 구현해보자. State 패턴에서는 상태를 저장할 변수가 필요하다. 따라서 state 변수를 만들어 시계의 상태를 저장한다. state변수에는 State 인터페이스를 구현한 클래스들(쉽게말해 상태들)을 저장할 수 있다. 

 다음으로 생성자를 통하여 시계가 만들어졌을때 상태를 지정한다. 또 setState() 함수를 이용하여 시계의 상태를 바꿀수 있도록 setter를 만들어 주자.

 마지막으로 Clock의 상태에 따라 메소드가 호출될 수 있도록 normal 메소드와 alarm 메소드를 만든다.

 

 각각의 상태들은 State 인터페이스를 상속받아 구현한다. 먼저 Normal State일 때를 정의해보자. 

  Normal State에선 normal 메소드를 호출해봤자 아무 의미가 없다. 따라서 normal 메소드는 구현하지 않는다(그러나 여기선 작동하는지 확인하기 위하여 출력문을 넣었다). 다음으로 alarm 메소드를 호출하면 setState() 메소드를 통해 Alarm State로 변경한다. 


 Alarm State에서는 5분 동안 알람을 울린 후 다시 Normal State로 변경하도록 하자. Alarm State일때 normal 메소드를 호출하면 5분 후 normal State로 상태를 변경한다.

 위 예제에서는 구현의 간소화를 위해 int 형으로 min을 표기했다. 제대로 만들어 보고 싶은 사람은 쓰레드나 타이머를 이용해서 구현해 보도록 하자.


이제 메인 함수에서 시계를 작동시켜 보자.

 Clock 인스턴스를 만든 후 비교를 위해 normal State일때 normal 메소드를 alarm 상태일때 normal 메소드를 호출해 보았다. 상태에 따라 같은 메소드라도 다른 결과가 나타난다는 것을 확인할 수 있다.


 State 패턴을 왜 사용하는지 알고싶으면 위 예제를 if~else 또는 switch~case 문을 이용하여 만들어 보면 알게 될 것이다(상태가 많아짐에 따라 무수한 분기문을 볼 수 있으며 다른 상태를 집어넣기 위해 수정해야 할 부분이 상당하다).

 자세히 보면 State Pattern은 Strategy Pattern(전략 패턴)과 많이 유사한 것을 확인할 수 있다. 그러나 두 패턴에는 차이점이 있다. 전략 패턴의 경우 전략 변경에 일정함이 없지만, 스테이트 패턴의 경우 각 State간에 일정한 규칙을 가지고 변화한다는 차이점이 있다. 

 여담으로 setState() 메소드를 통해 상태를 바꿀때 마다 new를 이용하여 State 객체를 새로 생성하는 모습을 볼 수 있다. 이 경우 메모리의 낭비로 인해 성능 저하를 초래할 가능성이 있다. 생각해보면 State 객체는 하나만 존재해도 상관없다는 특성을 지니고 있다. 따라서 State 객체들은 싱글턴 패턴을 적용하여 구현하면 좀 더 효율적으로 코드를 개선할 수 있을 것이다.

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

State Pattern (스테이트 패턴)  (2) 2015.12.17
Singleton Pattern (싱글턴 패턴)  (2) 2015.12.17
SOLID 원칙  (3) 2015.12.14
  • 강석렬 2015.12.18 11:51 신고 댓글주소 수정/삭제 댓글쓰기

    좋은 글 잘 봤습니다~~^^

    그런데 제가 보기에는 위 내용의 예저는 구현과 인터페이스를 분리해서 구현하는 브릿지 패턴으로 오해할 부분이 있어보입니다.

    Clock 이라는 인터페이스 군이 state라는 구현 객체군을 들고 있어서 그 구조는 브릿지 패턴이기 때문입니다.

    state 를 상속받은 NormalState, AlarmState 이 부분에 대한 설명까지만 하고 Clock이라는 객체가 감싸서 사용하는 방식이 아니라 직접 State 인터페이스를 통해서만 사용하는 방식만을 보여줬으면 어땠을까 합니다~^^;

    다시 요약 해서 말씀드리자면 state의 설명이 잘 못 됐다는 것은 아니고 예시로 작성한 구조적인 부분에서 브릿지 패턴 부분이 들어가 있어서 패턴을 처음 보시는 분들이 브릿지 패턴을 스테이트 패턴으로 오해하지 않을까 합니다~~^^

    좋은 글 잘 봤습니다~~^^

    추가로 브릿지 패턴에 대한 설명이 나와있는 아래 주소를 추가합니다~^^
    https://ko.wikipedia.org/wiki/%EB%B8%8C%EB%A6%AC%EC%A7%80_%ED%8C%A8%ED%84%B4

  • 잘 봤습니다~