Menu
Coddy logo textTech

커맨드 패턴

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