상태 패턴
Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 70개 중 60번째.
상태 패턴(State pattern)은 객체의 내부 상태가 변경될 때 객체가 동작을 변경할 수 있도록 하는 행동 패턴입니다. 여러 상태를 처리하기 위해 복잡한 조건문을 사용하는 대신, 각 상태를 별도의 클래스로 캡슐화하고 동작을 현재 상태 객체에 위임합니다.
이 패턴은 세 가지 구성 요소로 이루어집니다: 상태별 동작을 정의하는 상태 인터페이스, 해당 동작을 구현하는 구체적인 상태 클래스들, 그리고 현재 상태에 대한 참조를 유지하는 컨텍스트 클래스입니다:
public interface IDocumentState
{
void Publish(Document doc);
}
public class DraftState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Moving to moderation");
doc.SetState(new ModerationState());
}
}
public class ModerationState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Publishing document");
doc.SetState(new PublishedState());
}
}
public class PublishedState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Already published");
}
}컨텍스트는 현재 상태를 유지하고 작업을 해당 상태로 위임합니다. 상태 객체는 컨텍스트에서 SetState()를 호출하여 전환을 트리거할 수 있습니다:
public class Document
{
private IDocumentState _state = new DraftState();
public void SetState(IDocumentState state) => _state = state;
public void Publish() => _state.Publish(this);
}
var doc = new Document();
doc.Publish(); // 검토 단계로 이동
doc.Publish(); // 문서 발행 중
doc.Publish(); // 이미 발행됨상태 패턴은 현재 상태를 확인하는 방대하게 퍼져 있는 if-else 또는 switch 문을 제거합니다. 각 상태 클래스는 자신의 동작만을 처리하므로, 코드를 유지 관리하기 쉽게 만들고 새로운 상태를 추가하여 확장하기 용이하게 합니다.
챌린지
쉬움상태(State) 패턴을 사용하여 신호등 시스템을 만들어 보겠습니다. 신호등은 서로 다른 상태(빨간색, 노란색, 초록색)를 순환하며, 각 상태는 신호가 바뀔 때 어떤 일이 일어날지를 결정합니다. 복잡한 조건문을 사용하는 대신, 각 신호 상태를 고유한 클래스로 캡슐화할 것입니다.
코드는 세 개의 파일로 구성됩니다:
TrafficLightState.cs:Traffic네임스페이스 안에Change(TrafficLight light)메서드를 가진ITrafficLightState인터페이스를 정의합니다. 그런 다음 세 개의 구체적인 상태 클래스를 만듭니다:RedState-Change가 호출되면,Red - Stop! Changing to Green...을 출력하고 신호를GreenState로 전환합니다.GreenState-Green - Go! Changing to Yellow...를 출력하고YellowState로 전환합니다.YellowState-Yellow - Caution! Changing to Red...를 출력하고RedState로 전환합니다.
TrafficLight.cs: 동일한 네임스페이스에TrafficLight클래스를 만듭니다. 이것은 현재 상태를 유지하는 컨텍스트(context) 클래스입니다. 기본적으로RedState에서 시작해야 하며 다음을 포함해야 합니다:- 상태 객체들이 전환을 트리거하는 데 사용하는
SetState(ITrafficLightState state)메서드 - 현재 상태의
Change메서드에 위임하는Change()메서드
- 상태 객체들이 전환을 트리거하는 데 사용하는
Program.cs:TrafficLight를 생성하고 입력에 따라 상태 변화를 순환시켜 모든 것을 하나로 묶습니다.
하나의 입력을 받게 됩니다:
- 신호등을 변경할 횟수 (정수)
TrafficLight(빨간색으로 시작)를 생성하고 지정된 횟수만큼 Change()를 호출하세요.
예를 들어, 입력이 3이면 출력은 다음과 같아야 합니다:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...입력이 5이면 신호가 다시 순환합니다:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...TrafficLight 클래스에는 각 색상의 의미나 다음에 무엇이 올지에 대한 로직이 전혀 포함되어 있지 않다는 점에 주목하세요. 모든 지식은 개별 상태 클래스에 들어 있습니다. 각 상태는 자신의 동작과 다음 상태로의 전환을 트리거하는 책임을 집니다!
치트 시트
상태 패턴(State pattern)은 객체의 내부 상태가 변경될 때 객체가 동작을 변경할 수 있도록 하는 행동 패턴입니다. 복잡한 조건문을 사용하는 대신, 각 상태를 별도의 클래스로 캡슐화하고 현재 상태 객체에 동작을 위임합니다.
이 패턴은 세 가지 구성 요소로 이루어집니다:
- 상태 인터페이스(State interface): 상태별 동작을 정의합니다.
- 구체적인 상태 클래스(Concrete state classes): 각 상태에 대한 동작을 구현합니다.
- 컨텍스트 클래스(Context class): 현재 상태에 대한 참조를 유지합니다.
구현 예시:
// 상태 인터페이스
public interface IDocumentState
{
void Publish(Document doc);
}
// 구체적인 상태 클래스
public class DraftState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Moving to moderation");
doc.SetState(new ModerationState());
}
}
public class ModerationState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Publishing document");
doc.SetState(new PublishedState());
}
}
public class PublishedState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Already published");
}
}
// 컨텍스트 클래스
public class Document
{
private IDocumentState _state = new DraftState();
public void SetState(IDocumentState state) => _state = state;
public void Publish() => _state.Publish(this);
}
// 사용법
var doc = new Document();
doc.Publish(); // 검토 단계로 이동
doc.Publish(); // 문서 발행 중
doc.Publish(); // 이미 발행됨컨텍스트는 현재 상태를 보유하고 동작을 해당 상태에 위임합니다. 상태 객체는 컨텍스트의 SetState()를 호출하여 상태 전환을 트리거할 수 있습니다. 이를 통해 무분별한 if-else 또는 switch 문을 제거하여 코드를 더 쉽게 유지 관리하고 새로운 상태로 확장할 수 있습니다.
직접 해보기
using System;
using Traffic;
class Program
{
public static void Main(String[] args)
{
// 신호등을 변경할 횟수를 읽어옵니다
int n = Convert.ToInt32(Console.ReadLine());
// TODO: `TrafficLight` 인스턴스를 생성합니다 (기본적으로 `RedState`에서 시작함)
// TODO: 반복문을 사용하여 `Change()` 메서드를 `n`번 호출합니다
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.