1.3. Motivation ¶
네트워크 커넥션을 나타내는 TCPConnection 라는 클래스를 생각해보자. TCPConnection 객체는 여러가지의 상태중 하나 일 수 있다. (Established, Listening, Closed). TCPConnection 객체가 다른 객체로부터 request를 받았을 때, TCPConnection 은 현재의 상태에 따라 다르게 응답을 하게 된다. 예를 들어 'Open' 이라는 request의 효과는 현재의 상태가 Closed 이냐 Established 이냐에 따라 다르다. StatePattern은 TCPConnection 이 각 상태에 따른 다른 행위들을 표현할 수 있는 방법에 대해 설명한다.
StatePattern 의 주된 아이디어는 네트워크 커넥션의 상태를 나타내는 TCPState 추상클래스를 도입하는데에 있다. TCPState 클래스는 각각 다른 상태들을 표현하는 모든 클래스들에 대한 일반적인 인터페이스를 정의한다. TCPState의 서브클래스는 상태-구체적 행위들을 구현한다. 예를 들어 TCPEstablished 는 TCPConnection 의 Established 상태를, TCPClosed 는 TCPConnection 의 Closed 상태를 구현한다.
TCPConnection 클래스는 TCP 커넥션의 현재 상태를 나타내는 state 객체를 가지고 있다. (TCPState 서브클래스의 인스턴스) TCPConnection 은 이 state 객체에게 모든 상태-구체적 request들을 위임 (delegate) 한다. TCPConnection 은 커넥션의 특정 상태에 대한 명령들을 수행하기 위해 TCPState 서브클래스 인스턴스를 이용한다.
커넥션이 상태를 전환할 경우, TCPConnection 객체는 사용하고 있는 state 객체를 바꾼다. 예를 들어 커넥션이 established 에서 closed 로 바뀌는 경우 TCPConnection 은 현재의 TCPEstablished 인스턴스를 TCPClosed 인스턴스로 state 객체를 교체한다.
1.4. Applicability ¶
다음과 같은 경우에 StatePattern 을 이용한다.
- 객체의 행위가 객체의 상태에 의존적일때. 그리고 객체가 run-time 시에 상태에 따라 행위를 바꾸어야 할 경우.
- 객체의 상태에 대한 처리를 위해 구현하는 다중 조건 제어문이 거대해질 경우. 이 상태들을 일반적으로 하나나 그 이상의 열거형 상수들로 표현된다. 종종 여러 명령들은 객체 상태에 따른 처리를 위해 비슷한 유형의 조건 제어와 관련한 코드를 가지게 된다. StatePattern 은 각각의 조건분기점들을 클래스로 분리시킨다. 이는 객체의 상태들을 다른 객체로부터 다양하게 독립적일 수 있는, 고유의 권리를 가지는 객체로서 취급하도록 해준다.
1.7. Collaborations ¶
- Context는 상태-구체적 request들을 현재의 ConcreteState 객체에 위임한다.
- context는 request를 다루는 State 객체에게 인자로서 자기 자신을 넘길 수 있다. 이는 필요한 경우 State 객체들로 하여금 context 에 접근할 수 있도록 해준다.
- Context는 클라이언트의 주된 인터페이스이다. 클라이언트들은 State 객체들과 함께 context 를 설정할 수 있다. 일단 context가 설정되면, context 의 클라이언트는 State 객체들을 직접적으로 다룰 필요가 없다.
- Context 나 ConcreteState 서브클래스는 상황에 따라 state를 결정할 수 있다.
1.8. Consequences ¶
StatePattern은 다음과 같은 결과를 가진다.
- It localizes state-specific behavior and partitions bahavior for different states.
- It makes state transitions explicit.
- State objects can be shared.
1.9. Implementation ¶
- Who defines the state transitions?
- A table-based alternative.
- Creating and destroying State objects.
- Using dynamic inheritance.
1.10. Sample Code ¶
다음의 예제는 앞서 Motivation 에서 언급했었던 TCP 커낵션에 대한 C++ 코드의 예이다. 이 에제는 TCP 프로토콜에 대해 단순화 시킨 것이므로, TCP 커넥션들의 모든 상태나 프토토콜 전체를 설명하지 않는다. (이 예제는 Lynch 와 Rose LR93 에 의해 설명된 TCP 커넥션 프로토콜에 기초한 것이다)
~cpp class TCPOctectStream; class TCPState; class TCPConnection { public: TCPConnection (); void ActiveOpen (); void PassiveOpen (); void Close (); void Send (); void Acknowledge (); void Synchronize (); void ProcessOctet (TCPOctetStream* ); private: friend class TCPState; void ChangeState (TCPState* ); private: TCPState* _state; };
TCPConnection 은 _state 멤버변수를 이용, TCPState 의 인스턴스를 유지한다. TCPState 클래스는 TCPConnection 의 상태-전환 인터페이스를 중복하여 가진다. 각각의 TCPState 명령들은 TCPConnection 인스턴스를 인자로서 취하며, TCPState 로 하여금 TCPConnection 으로부터 데이터를 접근하거나 현재 커넥션의 상태를 전환할 수 있도록 한다.
~cpp class TCPState { public: virtual void Transmit (TCPConnection* , TCPOctetStream* ): virtual void ActiveOpen (TCPConnection* ); virtual void PassiveOpen (TCPConnection* ); virtual void Close (TCPConnection* ); virtual void Synchronize (TCPConnection* ); virtual void Acknowledge (TCPConnection* ); virtual void Send (TCPConnection* ); protected: void ChangeState (TCPConnection* , TCPState* ); };
TCPConnection 은 상태-구체적 request들으 TCPState 인스턴스인 _state 에 위임한다. TCPConnection 은 또한 이 변수를 새로운 TCPState 로 전환하는 명령을 제공한다. TCPConnection 의 생성자는 _state를 TCPClosed 상태로 초기화한다. (후에 정의된다.)
~cpp TCPConnection::TCPConnection () { _state = TCPClosed::Instance (); } void TCPConnection::ChangeState (TCPState* s) { _state = s; } void TCPConnection::ActiveOpen () { _state->ActiveOpen (this); } void TCPConnection::PassiveOpen () { _state->PassiveOpen (this); } void TCPConnection::Close () { _state->Close (this); } void TCPConnection::Acknowledge () { _state->Acknowledge (this); } void TCPConnection::Synchronize () { _state->Synchronize (this); }
TCPState 는 위임받은 모든 request 에 대한 기본 행위를 구현한다. TCPState는 또한 ChnageState 명령으로써 TCPConnection 의 상태를 전환할 수 있다. TCPState 는 TCPConnection 의 friend class 로 선언되어진다. 이로써 TCPState 는 TCPConnection 의 명령들에 대한 접근 권한을 가진다.
~cpp void TCPState::Transmit (TCPConnection*, TCPOctetStream* ) { } void TCPState::ActiveOpen (TCPConnection* ) { } void TCPState::PassiveOpen (TCPConnection* ) { } void TCPState::Close (TCPConnection* ) { } void TCPState::Synchronize (TCPConnection* ) { } void TCPState::ChangeState (TCPConnection* t, TCPState* s) { t->ChangeState (s); }
TCPState의 서브클래스들은 상태-구체적 행위들을 구현한다. TCP 커넥션은 다양한 상태일 수 있다. Established, Listen, Closed 등등. 그리고 각 상태들에 대한 TCPState 의 서브클래스들이 있다. 여기서는 3개의 서브클래스들을 다룰 것이다. (TCPEstablished, TCPListen, TCPClosed)
~cpp class TCPEstablished : public TCPState { public: static TCPState* Instance (); virtual void Transmit (TCPConnection* , TCPOctetStream* ); virtual void Close (TCPConnection* ); }; class TCPListen : public TCPState { public: static TCPState* Instance (); virtual void Send (TCPConnection* ); // ... }; class TCPClosed : public TCPState { public: static TCPState* Instance (); virtual void ActiveOpen (TCPConnection* ); virtual void PassiveOpen (TCPConnection* ); // ... };
TCPState 서브클래스는 내부 상태를 가지지 않는다, 그러므로 TCPState는 공유될 수 있고, 각각 단지 하나의 인스턴스만이 요구되어진다. 이 TCPState 서브클래스의 각각의 유일한 인스턴스들은 정적함수인 Instance 로 얻어진다. (TCPState 서브클래스는 Singleton 으로 만들어진다.)
각각의 TCPState 서브클래스는 해당 상태에 알맞는 request 에 대한 상태-구체적 행위들을 구현한다.
~cpp void TCPClosed::ActiveOpen (TCPConnection* t) { // send SYN, receive SYN, ACK, etc. ChangeState (t, TCPEstablished::Instance ()); } void TCPClosed::PassiveOpen (TCPConnection* t) { ChangeState (t, TCPListen::Instance ()); } void TCPEstablished::Close (TCPConnection* t) { // send FIN, receive ACK of FIN ChangeState (t, TCPListen::Instance ()); } void TCPEstablished::Transmit (TCPConnection* t, TCPOctetStream* o) { t->ProcessOctet (o); } void TCPListen::Send (TCPConnection* t) { // send SYN, receive SYN, ACK, etc. ChangeState (t, TCPEstablished::Instance ()); }
1.11. Known Uses ¶
대부분의 대중적인 상호작용적인 드로잉 프로그램들은 직접 조작하여 명령을 수행하는 'tool' 을 제공한다. 예를 들어, line-drawing tool 은 사용자가 클릭 & 드레그 함으로서 새 선을 그릴 수 있도록 해준다. selection tool 은 사용자가 도형을 선택할 수 있게 해준다. 보통 이러한 툴들의 palette (일종의 도구상자 패널)를 제공한다. 사용자는 이러한 행동을 'tool을 선택한 뒤 선택한 tool을 이용한다' 라고 생각한다. 하지만, 실제로는 editor 의 행위가 현재 선택한 tool로 전환되는 것이다. drawing tool 이 활성화 되었을 때 우리는 도형을 그리고 selection tool 이 활성화 되었을 때 도형을 선택할 수 있는 식이다. 우리는 현재 선택된 tool 에 따른 editor 의 행위를 전환시키는 부분에 대해 StatePattern 을 이용할 수 있다.
툴-구체적 행위를 구현하는 서브클래스를 정의하는 곳에 대해 Tool 추상 클래스를 정의할 수 있다. drawing editor 는 currentTool 객체를 가지며, request를 이 객체에 위임시킨다. 사용자가 새 tool를 골랐을 때, drawing editor 는 행위를 전환해야 하므로 따라서 이 객체는 교체된다.
이 방법은 HowDraw Joh92와 Unidraw VL90 drawing editor 프레임워크에 이용되었다. 이는 클라이언트로 하여금 새로운 종류의 tool들을 쉽게 정의할 수 있도록 해준다. HowDraw 에서 DrawingController 클래스는 currentTool 객체에게 request를 넘긴다. UniDraw에서는 각각 Viewer 와 Tool 클래스가 이와 같은 관계를 가진다. 다음의 클래스 다이어그램은 Tool 과 DrawingController 인터페이스에 대한 설명이다.
Coplien's Envelope-Letter idiom Cop92 는 State 와 관련되어있다. Envelope-Letter 는 run-time 시에 객체의 클래스를 전환하는 방법이다. StatePattern 은 더 구체적이며, 객체의 상태에 그 행위가 의족적일때에 다루는 방법에 촛점을 맞춘다.