Menu
CoddyTech

커맨드 패턴

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

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

Go에서는 Execute 메서드를 사용하는 Command 인터페이스를 정의한 다음, 작업을 수행하는 데 필요한 모든 정보를 보유하는 구체적인 명령을 생성합니다:

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 패턴을 사용해 만들어 봅시다! 텍스트 작업을 encapsulates하는 명령을 만들고, 편집기가 작업을 실행하고 되돌릴 수 있도록 합니다. 이는 명령이 왜 그렇게 강력한지 보여 주는 전형적인 사용 사례입니다.

코드를 세 개의 파일로 구성합니다.

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

    두 개의 메서드를 가진 Command 인터페이스를 Create합니다. 작업을 수행하는 Execute() string과 작업을 되돌리는 Undo() string입니다.

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

    • InsertCommand: 편집기 포인터와 삽입할 텍스트를 holds합니다. Execute는 텍스트를 편집기의 Content에 appends하고 Inserted: [text]를 반환합니다. Undo는 끝에서 해당 텍스트를 제거하고 Undone insert: [text]를 반환합니다.
    • DeleteCommand: 편집기 포인터와 끝에서 delete할 characters의 count를 holds합니다. Execute는 해당 characters를 제거하고(Undo를 위해 저장함) Deleted: [removed text]를 반환합니다. Undo는 해당 텍스트를 복원하고 Undone delete: [restored text]를 반환합니다.
  • editor.go: 텍스트 편집기(receiver)와 명령 history를 관리하는 호출자를 Create합니다.

    Content field(string)와 Append(text string) 및 DeleteLast(count int) string 메서드(삭제된 텍스트를 반환)를 가진 TextEditor 구조체를 Build합니다.

    실행된 명령의 slice를 history로 holds하는 EditorInvoker 구조체를 Build합니다. 다음 메서드를 Add합니다.

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

    작업 count를 읽습니다. 각 작업에 대해 작업 유형(insert, delete 또는 undo)을 읽습니다. insert의 경우 삽입할 텍스트도 읽습니다. delete의 경우 characters count를 읽습니다. 각 작업을 invoker를 통해 Execute하고 결과를 출력합니다. 모든 작업이 끝나면 최종 편집기 Content를 Final: [content]로 출력합니다.

다음 입력이 제공됩니다.

  • Line 1: 작업 수
  • 각 작업: 작업 유형, 필요한 경우 추가 데이터(insert의 텍스트, delete의 count)

예를 들어, 다음이 주어졌다고 합시다.

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

각 명령이 자신의 작업을 수행하고 되돌리는 데 필요한 모든 것을 어떻게 encapsulates하는지 확인해 보세요. invoker는 명령이 무엇을 하는지 알지 못합니다. 그저 명령을 Execute하고 Undo 지원을 위해 history를 유지합니다!

직접 해보기

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 컴파일러