커맨드 패턴
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(); // 전등이 꺼짐커맨드 패턴은 실행 취소 기능, 매크로 기록 또는 나중에 실행하기 위한 작업 큐잉을 구현하는 데 특히 강력합니다. 각 커맨드는 작업을 수행하는 데 필요한 모든 것을 캡슐화하여 시스템을 더 유연하고 확장 가능하게 만듭니다.
챌린지
쉬움커맨드 패턴을 사용하여 텍스트 에디터 명령 시스템을 구축해 보겠습니다. 텍스트 삽입 및 삭제와 같은 편집 작업을 커맨드 객체로 캡슐화하여, 에디터가 통합된 인터페이스를 통해 이를 실행할 수 있는 시스템을 만들 것입니다.
코드는 다음 세 개의 파일로 구성됩니다:
Command.cs:TextEditor네임스페이스 안에 단일 메서드Execute()를 가진ICommand인터페이스를 정의합니다. 그런 다음 두 개의 구체적인 커맨드 클래스를 생성합니다:InsertCommand- 생성자에서Document와 문자열text를 받습니다. 실행 시, 문서에 텍스트를 추가하고Inserted: {text}를 출력합니다.DeleteCommand- 생성자에서Document와int 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}" 형식으로 최종 내용을 출력하세요
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.