U E D R , A S I H C RSS

Gof/State

1. State

1.1. Intent

객체의 내부 상태가 바뀌었을 때 객체의 행동을 바꿀 객체를 허용한다. 객체는 마치 객체의 클래스가 변경된 것처럼 보일 것이다.

1.2. Also Known As

Objects for States

1.3. Motivation

네트워크 커넥션을 나타내는 TCPConnection 라는 클래스를 생각해보자. TCPConnection 객체는 여러가지의 상태중 하나 일 수 있다. (Established, Listening, Closed). TCPConnection 객체가 다른 객체로부터 request를 받았을 때, TCPConnection 은 현재의 상태에 따라 다르게 응답을 하게 된다. 예를 들어 'Open' 이라는 request의 효과는 현재의 상태가 Closed 이냐 Established 이냐에 따라 다르다. StatePatternTCPConnection 이 각 상태에 따른 다른 행위들을 표현할 수 있는 방법에 대해 설명한다.

StatePattern 의 주된 아이디어는 네트워크 커넥션의 상태를 나타내는 TCPState 추상클래스를 도입하는데에 있다. TCPState 클래스는 각각 다른 상태들을 표현하는 모든 클래스들에 대한 일반적인 인터페이스를 정의한다. TCPState의 서브클래스는 상태-구체적 행위들을 구현한다. 예를 들어 TCPEstablishedTCPConnection 의 Established 상태를, TCPClosedTCPConnection 의 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.6. Participants

  • Context (TCPConnection)
    • 클라이언트들이 원하는 인터페이스를 정의한다.
    • 현재 상태를 정의하는 ConcreteState 서브클래스의 인스턴스를 가진다.
  • State (TCPState)
    • Context 의 특정 상태와 관련된 행위들을 캡슐화 하기 위한 관련 인터페이스를 정의한다.
  • ConcreteState subclass (TCPEstablished, TCPListen, TCPClosed)
    • 각각의 서브클래스들은 Context의 상태들과 관련된 행위들을 구현한다.

1.7. Collaborations

  • Context는 상태-구체적 request들을 현재의 ConcreteState 객체에 위임한다.
  • context는 request를 다루는 State 객체에게 인자로서 자기 자신을 넘길 수 있다. 이는 필요한 경우 State 객체들로 하여금 context 에 접근할 수 있도록 해준다.
  • Context는 클라이언트의 주된 인터페이스이다. 클라이언트들은 State 객체들과 함께 context 를 설정할 수 있다. 일단 context가 설정되면, context 의 클라이언트는 State 객체들을 직접적으로 다룰 필요가 없다.
  • Context 나 ConcreteState 서브클래스는 상황에 따라 state를 결정할 수 있다.

1.8. Consequences

StatePattern은 다음과 같은 결과를 가진다.
  1. It localizes state-specific behavior and partitions bahavior for different states.
  2. It makes state transitions explicit.
  3. State objects can be shared.

1.9. Implementation

  1. Who defines the state transitions?
  2. A table-based alternative.
  3. Creating and destroying State objects.
  4. Using dynamic inheritance.

1.10. Sample Code

다음의 예제는 앞서 Motivation 에서 언급했었던 TCP 커낵션에 대한 C++ 코드의 예이다. 이 에제는 TCP 프로토콜에 대해 단순화 시킨 것이므로, TCP 커넥션들의 모든 상태나 프토토콜 전체를 설명하지 않는다. (이 예제는 Lynch 와 Rose LR93 에 의해 설명된 TCP 커넥션 프로토콜에 기초한 것이다)

일단, 우리는 TCPConnection 클래스를 정의한다. TCPConnection 은 데이터를 전달하고 상태 전환을 위한 request를 다루는 인터페이스를 제공한다.

~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 의 상태를 전환할 수 있다. TCPStateTCPConnection 의 friend class 로 선언되어진다. 이로써 TCPStateTCPConnection 의 명령들에 대한 접근 권한을 가진다.

~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 ());
}

상태-구체적 작업들이 수행된 뒤, 이 명령들은 TCPConnection 의 상태를 전환하기 위해 ChangeState 명령을 호출한다. TCPConnection 은 TCP 커넥션 프로토콜에 대해 모른다. TCP에 대한 각각의 상태전환과 행동들을 정의하는 것은 TCPState 서브클래스들이다.

1.11. Known Uses

Johnson an Zweig JZ91StatePattern 과 TCP 커넥션 프로토콜로 어플리케이션의 특성을 나타낸다.

대부분의 대중적인 상호작용적인 드로잉 프로그램들은 직접 조작하여 명령을 수행하는 '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 은 더 구체적이며, 객체의 상태에 그 행위가 의족적일때에 다루는 방법에 촛점을 맞춘다.

1.12. Related Patterns

  • FlyweightPattern 은 State객체들이 언제 어떻게 공유되는지 표현한다.
  • State객체는 종종 SingletonPattern 으로 구현된다.


Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:23:18
Processing time 0.0444 sec