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.0278 sec