Menu
CoddyTech

커맨드 패턴

Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 107개 중 91번째.

Command 패턴은 요청을 객체로 캡슐화하여 작업을 매개변수화하거나 대기열에 추가하거나 실행 취소 기능을 지원할 수 있게 합니다. Strategy가 알고리즘을 캡슐화하는 반면, Command는 매개변수와 함께 전체 작업을 캡슐화합니다.

Go에서는 Command 인터페이스와 Execute 메서드를 정의한 다음, 작업을 수행하는 데 필요한 모든 정보를 담는 구체적인 명령을 만듭니다.

type Command interface {
    Execute() string
}

type Light struct {
    IsOn bool
}

type TurnOnCommand struct {
    light *Light
}

func (c *TurnOnCommand) Execute() string {
    c.light.IsOn = true
    return "Light turned on"
}

type TurnOffCommand struct {
    light *Light
}

func (c *TurnOffCommand) Execute() string {
    c.light.IsOn = false
    return "Light turned off"
}

호출자는 명령이 무엇을 하는지 알지 못한 채 명령을 저장하고 실행합니다:

type RemoteControl struct {
    command Command
}

func (r *RemoteControl) SetCommand(c Command) {
    r.command = c
}

func (r *RemoteControl) PressButton() string {
    return r.command.Execute()
}

호출자는 수신자(Light)와 완전히 분리되어 있습니다:

light := &Light{}
remote := &RemoteControl{}

remote.SetCommand(&TurnOnCommand{light: light})
fmt.Println(remote.PressButton())  // 조명이 켜짐

remote.SetCommand(&TurnOffCommand{light: light})
fmt.Println(remote.PressButton())  // 조명이 꺼짐

Command는 작업을 저장하거나 지연하거나 재생해야 하는 실행 취소/다시 실행 시스템, 작업 큐 또는 매크로 기록을 구현하는 데 이상적입니다.

challenge icon

챌린지

쉬움

Command 패턴을 사용해 실행 취소 기능이 있는 텍스트 편집기를 만들어 봅시다! 텍스트 작업을 캡슐화하는 명령을 만들어 편집기가 작업을 실행하고 되돌릴 수 있도록 합니다. 명령이 왜 그렇게 강력한지 보여주는 전형적인 사용 사례입니다.

코드를 세 개의 파일로 나누어 작성합니다.

  • command.go: 명령 인터페이스와 구체적인 텍스트 편집 명령을 정의합니다.

    두 가지 메서드가 있는 Command 인터페이스를 만듭니다. 작업을 수행하는 Execute() string과 작업을 되돌리는 Undo() string입니다.

    *TextEditor에서 작동하는 두 가지 명령 유형을 구현합니다.

    • InsertCommand: 편집기 포인터와 삽입할 텍스트를 보유합니다. Execute는 텍스트를 편집기의 내용 끝에 추가하고 Inserted: [text]를 반환합니다. Undo는 끝에서 해당 텍스트를 제거하고 Undone insert: [text]를 반환합니다.
    • DeleteCommand: 편집기 포인터와 끝에서 삭제할 문자 수를 보유합니다. Execute는 해당 문자를 제거하고(실행 취소를 위해 저장) Deleted: [removed text]를 반환합니다. Undo는 해당 문자를 복원하고 Undone delete: [restored text]를 반환합니다.
  • editor.go: 텍스트 편집기(리시버)와 명령 기록을 관리하는 인보커를 만듭니다.

    Content 필드(string)와 Append(text string), DeleteLast(count int) string 메서드(삭제된 텍스트를 반환)가 있는 TextEditor 구조체를 작성합니다.

    실행된 명령의 슬라이스를 기록으로 보유하는 EditorInvoker 구조체를 작성합니다. 다음 메서드를 추가합니다.

    • ExecuteCommand(cmd Command) string: 명령을 실행하고, 기록에 추가한 다음, 결과를 반환합니다.
    • UndoLast() string: 기록에서 마지막 명령을 제거하고, 해당 명령의 Undo를 호출한 다음, 결과를 반환합니다. 기록이 비어 있으면 Nothing to undo를 반환합니다.
  • main.go: 일련의 작업을 통해 명령 시스템을 시연합니다.

    작업 개수를 읽습니다. 각 작업에 대해 작업 유형(insert, delete 또는 undo)을 읽습니다. insert의 경우 삽입할 텍스트도 읽습니다. delete의 경우 문자 수를 읽습니다. 인보커를 통해 각 작업을 실행하고 결과를 출력합니다. 모든 작업이 끝나면 편집기의 최종 내용을 Final: [content] 형식으로 출력합니다.

다음 입력이 제공됩니다.

  • 1행: 작업 개수
  • 각 작업에 대해: 작업 유형, 필요한 경우 추가 데이터(insert의 텍스트, delete의 문자 수)

예를 들어, 다음 입력이 주어지면:

5
insert
Hello
insert
 World
delete
3
undo
undo

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

Inserted: Hello
Inserted:  World
Deleted: rld
Undone delete: rld
Undone insert:  World
Final: Hello

그리고 다음 입력이 주어지면:

4
insert
Go
insert
Lang
undo
insert
!

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

Inserted: Go
Inserted: Lang
Undone insert: Lang
Inserted: !
Final: Go!

그리고 다음 입력이 주어지면:

2
undo
insert
Test

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

Nothing to undo
Inserted: Test
Final: Test

각 명령은 작업을 수행하고 되돌리는 데 필요한 모든 것을 캡슐화한다는 점에 주목하세요. 인보커는 명령이 무엇을 하는지 알지 못합니다. 그저 명령을 실행하고 실행 취소를 지원하기 위해 기록을 유지할 뿐입니다!

직접 해보기

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// 작업 개수를 읽습니다
	line, _ := reader.ReadString('\n')
	numOps, _ := strconv.Atoi(strings.TrimSpace(line))
	
	// editor와 invoker를 생성합니다
	editor := &TextEditor{}
	invoker := &EditorInvoker{}
	
	for i := 0; i < numOps; i++ {
		// 작업 유형을 읽습니다
		opLine, _ := reader.ReadString('\n')
		opType := strings.TrimSpace(opLine)
		
		var result string
		
		switch opType {
		case "insert":
			// 삽입할 텍스트를 읽습니다
			textLine, _ := reader.ReadString('\n')
			text := strings.TrimSuffix(textLine, "\n")
			
			// TODO: InsertCommand를 생성하고 invoker를 통해 실행합니다
			_ = text
			_ = editor
			
		case "delete":
			// 삭제할 문자 수를 읽습니다
			countLine, _ := reader.ReadString('\n')
			count, _ := strconv.Atoi(strings.TrimSpace(countLine))
			
			// TODO: DeleteCommand를 생성하고 invoker를 통해 실행합니다
			_ = count
			
		case "undo":
			// TODO: invoker에서 UndoLast를 호출합니다
			
		}
		
		fmt.Println(result)
	}
	
	// 최종 내용을 출력합니다
	fmt.Printf("Final: %s\n", editor.Content)
}
quiz icon실력 점검

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

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

직접 연습해 보세요: 온라인 Go 컴파일러