목차
상태(State)
상태(State) 디자인 패턴은 객체의 내부 상태에 따라 객체의 행동을 변화시키는 패턴이다. 객체가 특정 상태에 있을 때만 수행할 수 있는 행동이 있거나, 상태에 따라 같은 이벤트에 다르게 반응해야 할 때 사용한다. 상태 패턴의 핵심은 상태를 클래스로 캡슐화하고, 객체의 상태 변화에 따라 이러한 상태 클래스들을 교체함으로써 객체의 행동을 동적으로 변경하는 것이다.
패턴의 기본 구성요소
- Context(문맥): 현재 상태를 가지고 상태에 대한 사용자의 요청을 상태 객체로 위임한다.
- State(상태): 객체의 특정 상태에 대한 행동과 전환 조건을 정의하는 인터페이스다.
- Concrete State(구체 상태): 상태 인터페이스를 구현하는 클래스들로, 구체적인 상태별 행동 로직을 담당한다.
패턴 미적용 예시
다음 예시는 케릭터의 상태변화와 케릭터가 수행하는 작업을 다루고 있다.
공격이나 방어상태에서는 기본상태를 거치지 않고 반대의 상태로 넘어갈 수 없다는 전제가 깔려있다.
public class Character
{
string state = "normal"; // 캐릭터의 초기 상태
public void HandleInput(string input)
{
if (state == "normal")
{
if (input == "defense") state = "defending";
else if (input == "attack") state = "attacking";
}
else if (input == "normal") state = "normal";
}
public void PerformAction()
{
if (state == "defending") Console.WriteLine("방어 중");
else if (state == "attacking") Console.WriteLine("공격 중");
else Console.WriteLine("일반 상태");
}
}
이 코드는 상태를 문자열로 관리하고, 상태에 따른 행동을 조건문으로 처리한다. 상태가 추가되거나 변경될 때마다 조건문을 수정해야 하므로 유지보수가 어렵다.
패턴 적용 예시
public interface IState
{
void HandleInput(Character character, string input);
void PerformAction();
}
public class NormalState : IState
{
public void HandleInput(Character character, string input)
{
if (input == "defense") character.State = new DefendingState();
else if (input == "attack") character.State = new AttackingState();
}
public void PerformAction()
{
Console.WriteLine("일반 상태");
}
}
public class DefendingState : IState
{
public void HandleInput(Character character, string input)
{
if (input == "normal") character.State = new NormalState();
}
public void PerformAction()
{
Console.WriteLine("방어 중");
}
}
public class AttackingState : IState
{
public void HandleInput(Character character, string input)
{
if (input == "normal") character.State = new NormalState();
}
public void PerformAction()
{
Console.WriteLine("공격 중");
}
}
public class Character
{
public IState State { get; set; }
public Character()
{
State = new NormalState();
}
public void HandleInput(string input)
{
State.HandleInput(this, input);
}
public void PerformAction()
{
State.PerformAction();
}
}
class Program
{
static void Main(string[] args)
{
var character = new Character();
character.PerformAction(); // 일반 상태
character.HandleInput("defense");
character.PerformAction(); // 방어 중
character.HandleInput("normal");
character.PerformAction(); // 일반 상태
}
}
상태 패턴을 적용하면 각 상태를 별도의 클래스로 구현하여 관리할 수 있으며, 새로운 상태 추가나 변경 시 기존 코드를 수정할 필요가 없어진다. 이는 코드의 유지보수성과 확장성을 크게 향상시킨다.
참고) 다른 패턴과의 차이
상태(State) 패턴과 옵저버(Observer) 패턴, 그리고 발행-구독(Publish-Subscribe) 패턴은 모두 특정 객체의 상태 변화를 지켜본다는 점이 비슷하지만, 각각의 목적과 사용 방식에는 뚜렷한 차이가 있다.
상태 패턴
- 목적: 객체의 상태 변화에 따라 객체의 행동을 변경한다. 객체 내부 상태에 기반해 객체 행동이 달라지도록 설계한다.
- 사용 방식: 객체의 상태를 나타내는 별도의 클래스(상태 클래스)로 캡슐화하고, 객체의 상태 변경에 따라 이 상태 클래스들을 교체함으로써 다양한 행동을 동적으로 제공한다. 상태 패턴은 주로 객체의 상태가 적고, 상태에 따른 행동 변화가 명확할 때 유용하다.
옵저버 패턴
- 목적: 한 객체(주제)의 상태 변화를 관찰하고 있는 여러 옵저버(관찰자) 객체들에게 상태 변화를 자동으로 알림으로써, 객체 간의 일대다(1:N) 종속성을 정의한다.
- 사용 방식: 주제 객체는 자신의 상태 변화를 옵저버들에게 통지하는 인터페이스를 제공하고, 옵저버는 주제 객체로부터 상태 변화를 통지받아 필요한 동작을 수행한다. 이 패턴은 주로 분산 이벤트 핸들링 시스템에서 상태 변화를 여러 소비자에게 효과적으로 알려야 할 때 사용된다.
발행-구독 패턴
- 목적: 발행자가 메시지를 발행하면, 이 메시지에 대해 관심을 가지고 있는 구독자들에게 메시지를 전달하는 방식으로, 옵저버 패턴과 유사하게 일대다 의존성을 구현하지만, 메시지 브로커(또는 이벤트 버스)를 통해 간접적으로 메시지를 전달한다.
- 사용 방식: 발행-구독 패턴에서는 발행자와 구독자 사이에 메시지 브로커가 존재하며, 발행자는 메시지 브로커에 메시지를 발행하고, 구독자는 관심 있는 메시지 유형을 구독한다. 메시지 브로커는 해당 메시지 유형을 구독하는 모든 구독자에게 메시지를 전달한다. 이 패턴은 시스템 간의 결합도를 낮추고 확장성을 높이는 데 유용하다.
차이점
- 용도와 목적의 차이: 상태 패턴은 객체의 내부 상태 변경에 따른 행동의 변화를 관리하는 데 중점을 두는 반면, 옵저버와 발행-구독 패턴은 상태 변화 또는 이벤트 발생을 다수의 관심 객체나 시스템에 통지하는 데 중점을 둔다.
- 통신 방식의 차이: 상태 패턴은 객체 내부의 상태 관리에 초점을 맞추며, 직접적인 객체의 상태 변화에 따른 로직 실행을 다룬다. 반면, 옵저버 패턴은 주제(Subject)와 옵저버(Observer) 간의 직접적인 등록 및 알림 메커니즘을 통해, 주제의 상태 변화를 관찰하고 있는 하나 이상의 옵저버에게 이러한 변화를 알린다.
옵저버 패턴에서 주제는 옵저버에 대한 참조를 유지하며, 주제의 상태에 변화가 있을 때 등록된 모든 옵저버에게 알림을 보낸다. 이때, 옵저버는 주제의 상태 변경 사실을 알게 되고, 필요한 조치를 취할 수 있다. 이 과정에서 옵저버는 주제의 상태를 직접 조회할 수도 있고, 알림을 통해 상태 변화에 대한 세부 정보를 받을 수도 있다.
즉, 상태 패턴이 객체 자체의 행동 변화에 집중하는 내부적인 메커니즘을 제공하는 반면, 옵저버 패턴은 객체 간의 상태 공유와 통신을 위한 외부적인 메커니즘을 제공한다. 상태 패턴은 객체 스스로의 상태 변화에 따른 행동의 변화를 내부적으로 처리하는데 사용되고, 옵저버 패턴은 한 객체의 상태 변화가 다른 객체들에게 영향을 줄 때, 즉 일대다의 의존성을 관리할 때 사용된다.
'컴퓨터 과학 > 디자인패턴' 카테고리의 다른 글
[디자인패턴] 행위 패턴(11) : 템플릿 메소드(Template Method) (0) | 2024.03.25 |
---|---|
[디자인패턴] 행위 패턴(10) : 전략(Strategy) (0) | 2024.03.25 |
[디자인패턴] 행위 패턴(8) : 발행-구독(Publisher-Subscriber) (0) | 2024.03.25 |
[디자인패턴] 행위 패턴(7) : 옵저버(Observer) (1) | 2024.03.25 |
[디자인패턴] 행위 패턴(6) : 메멘토(Memento) (0) | 2024.03.25 |