Menu
Coddy logo textTech

상태 패턴

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 문을 제거합니다. 각 상태 클래스는 자신의 동작만을 처리하므로, 코드를 유지 관리하기 쉽게 만들고 새로운 상태를 추가하여 확장하기 용이하게 합니다.

challenge icon

챌린지

쉬움

상태(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`번 호출합니다
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨