Menu
Coddy logo textTech

커맨드 패턴

Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 70개 중 56번째.

커맨드 패턴(Command pattern)은 요청을 요청에 대한 모든 정보를 포함하는 독립적인 객체로 변환하는 행동 패턴입니다. 이러한 변환을 통해 요청을 메서드 인자로 전달하거나, 큐에 저장하거나, 로그를 남기거나, 실행 취소 가능한 작업을 지원할 수 있습니다.

이 패턴은 연산을 호출하는 객체와 연산을 수행하는 방법을 아는 객체를 분리합니다. 이 패턴은 네 가지 핵심 구성 요소인 커맨드 인터페이스(command interface), 구체적인 커맨드(concrete commands), 리시버(receiver, 실제 작업을 수행하는 객체), 그리고 인보커(invoker, 커맨드를 트리거하는 객체)로 구성됩니다.

public interface ICommand
{
    void Execute();
}

public class Light  // 수신자
{
    public void TurnOn() => Console.WriteLine("Light is ON");
    public void TurnOff() => Console.WriteLine("Light is OFF");
}

public class TurnOnCommand : ICommand
{
    private Light _light;
    
    public TurnOnCommand(Light light) => _light = light;
    
    public void Execute() => _light.TurnOn();
}

public class TurnOffCommand : ICommand
{
    private Light _light;
    
    public TurnOffCommand(Light light) => _light = light;
    
    public void Execute() => _light.TurnOff();
}

인보커(Invoker)는 명령이 무엇을 하는지 알지 못한 채 명령을 저장하고 실행합니다:

public class RemoteControl  // 인보커
{
    private ICommand _command;
    
    public void SetCommand(ICommand command) => _command = command;
    
    public void PressButton() => _command.Execute();
}

// 사용법
var light = new Light();
var remote = new RemoteControl();

remote.SetCommand(new TurnOnCommand(light));
remote.PressButton();  // 전등이 켜짐

remote.SetCommand(new TurnOffCommand(light));
remote.PressButton();  // 전등이 꺼짐

커맨드 패턴은 실행 취소 기능, 매크로 기록 또는 나중에 실행하기 위한 작업 큐잉을 구현하는 데 특히 강력합니다. 각 커맨드는 작업을 수행하는 데 필요한 모든 것을 캡슐화하여 시스템을 더 유연하고 확장 가능하게 만듭니다.

challenge icon

챌린지

쉬움

커맨드 패턴을 사용하여 텍스트 에디터 명령 시스템을 구축해 보겠습니다. 텍스트 삽입 및 삭제와 같은 편집 작업을 커맨드 객체로 캡슐화하여, 에디터가 통합된 인터페이스를 통해 이를 실행할 수 있는 시스템을 만들 것입니다.

코드는 다음 세 개의 파일로 구성됩니다:

  • Command.cs: TextEditor 네임스페이스 안에 단일 메서드 Execute()를 가진 ICommand 인터페이스를 정의합니다. 그런 다음 두 개의 구체적인 커맨드 클래스를 생성합니다:
    • InsertCommand - 생성자에서 Document와 문자열 text를 받습니다. 실행 시, 문서에 텍스트를 추가하고 Inserted: {text}를 출력합니다.
    • DeleteCommand - 생성자에서 Documentint count를 받습니다. 실행 시, 문서에서 마지막 count개의 문자를 제거하고 Deleted: {count} characters를 출력합니다.
  • Document.cs: 동일한 네임스페이스에 Document 클래스를 생성합니다. 이것은 수신자(receiver)로, 실제 텍스트 작업을 수행하는 객체입니다. 다음을 포함해야 합니다:
    • 콘텐츠를 저장할 프라이빗 문자열 필드 (빈 문자열로 초기화)
    • 콘텐츠에 텍스트를 추가하는 Append(string text) 메서드
    • 마지막 count개의 문자를 제거하는 RemoveLast(int count) 메서드 (만약 count가 콘텐츠 길이보다 크면 모든 콘텐츠를 삭제)
    • 현재 콘텐츠를 반환하는 GetContent() 메서드
  • Program.cs: 호출자(invoker) 역할을 하는 Editor 클래스를 생성합니다. 이 클래스는 SetCommand(ICommand command) 메서드와 현재 커맨드를 실행하는 ExecuteCommand() 메서드를 가져야 합니다. 메인 코드에서 일련의 편집 작업을 처리하여 커맨드 패턴을 시연합니다.

다음 입력을 받게 됩니다:

  • 실행할 커맨드의 수
  • 각 커맨드에 대해: 커맨드 타입(insert 또는 delete)과 이어서 삽입할 텍스트 또는 삭제할 문자 수

모든 커맨드를 실행한 후, 최종 문서 콘텐츠를 Content: {content} 형식으로 새 줄에 출력합니다.

예를 들어, 입력이 다음과 같다면:

4
insert
Hello
insert
 World
delete
3
insert
!

출력은 다음과 같아야 합니다:

Inserted: Hello
Inserted:  World
Deleted: 3 characters
Inserted: !
Content: Hello Wo!

Editor(호출자)가 텍스트 조작에 대해 전혀 알지 못한다는 점에 주목하세요. 단순히 주어진 커맨드를 실행할 뿐입니다. 커맨드는 작업과 수신자를 모두 캡슐화하므로, 에디터를 변경하지 않고도 ReplaceCommand와 같은 새로운 작업을 쉽게 추가할 수 있습니다!

치트 시트

커맨드 패턴(Command pattern)은 요청을 요청에 대한 모든 정보를 포함하는 독립적인 객체로 캡슐화하는 행동 패턴입니다. 이를 통해 요청을 메서드 인자로 전달하거나, 큐에 저장하거나, 로그를 남기거나, 실행 취소 가능한 작업을 지원할 수 있습니다.

이 패턴은 네 가지 핵심 구성 요소로 이루어집니다:

  • 커맨드 인터페이스(Command interface) - Execute() 메서드를 정의합니다
  • 구체적인 커맨드(Concrete commands) - 커맨드 인터페이스를 구현하고 특정 작업을 캡슐화합니다
  • 수신자(Receiver) - 실제 작업을 수행하는 객체입니다
  • 호출자(Invoker) - 구현 방식을 모른 채 커맨드를 트리거하는 객체입니다

커맨드 인터페이스 및 구체적인 커맨드

public interface ICommand
{
    void Execute();
}

public class TurnOnCommand : ICommand
{
    private Light _light;
    
    public TurnOnCommand(Light light) => _light = light;
    
    public void Execute() => _light.TurnOn();
}

public class TurnOffCommand : ICommand
{
    private Light _light;
    
    public TurnOffCommand(Light light) => _light = light;
    
    public void Execute() => _light.TurnOff();
}

수신자(Receiver)

public class Light
{
    public void TurnOn() => Console.WriteLine("Light is ON");
    public void TurnOff() => Console.WriteLine("Light is OFF");
}

호출자(Invoker)

public class RemoteControl
{
    private ICommand _command;
    
    public void SetCommand(ICommand command) => _command = command;
    
    public void PressButton() => _command.Execute();
}

사용법

var light = new Light();
var remote = new RemoteControl();

remote.SetCommand(new TurnOnCommand(light));
remote.PressButton();  // Light is ON

remote.SetCommand(new TurnOffCommand(light));
remote.PressButton();  // Light is OFF

커맨드 패턴은 실행 취소 기능, 매크로 기록 또는 나중에 실행하기 위해 작업을 큐에 넣는 기능을 구현할 때 유용합니다. 각 커맨드는 작업을 수행하는 데 필요한 모든 것을 캡슐화하므로 시스템을 더욱 유연하고 확장 가능하게 만듭니다.

직접 해보기

using System;
using TextEditor;

// TODO: Editor 클래스(invoker)를 생성하세요
public class Editor
{
    // TODO: 현재 명령을 저장할 private 필드를 추가하세요
    private ICommand _command;

    // TODO: SetCommand 메서드를 구현하세요
    public void SetCommand(ICommand command)
    {
        // TODO: 명령을 저장하세요
    }

    // TODO: ExecuteCommand 메서드를 구현하세요
    public void ExecuteCommand()
    {
        // TODO: 현재 명령을 실행하세요
    }
}

class Program
{
    public static void Main(string[] args)
    {
        // 명령의 개수를 읽습니다
        int n = Convert.ToInt32(Console.ReadLine());

        // document(receiver)를 생성합니다
        Document document = new Document();

        // editor(invoker)를 생성합니다
        Editor editor = new Editor();

        // TODO: 각 명령을 처리하세요
        for (int i = 0; i < n; i++)
        {
            string commandType = Console.ReadLine();

            // TODO: commandType에 따라 적절한 명령을 생성하세요
            // - "insert"인 경우: 텍스트를 읽고 InsertCommand를 생성하세요
            // - "delete"인 경우: 개수를 읽고 DeleteCommand를 생성하세요
            // 그런 다음 editor에 명령을 설정하고 실행하세요
        }

        // TODO: "Content: {content}" 형식으로 최종 내용을 출력하세요
    }
}
quiz icon실력 점검

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

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