Menu
Coddy logo textTech

커맨드 패턴

Coddy Python 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 64개 중 49번째.

커맨드 패턴(Command Pattern)은 요청을 객체로 캡슐화하여, 작업을 큐에 저장하거나 요청을 로그로 기록하고, 실행 취소(undo) 기능을 지원할 수 있게 합니다. 이는 작업을 호출하는 객체와 작업을 수행하는 객체를 분리합니다.

다음은 간단한 명령 클래스들입니다:

class Command:
    def execute(self):
        pass

class LightOnCommand(Command):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_on()

class LightOffCommand(Command):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_off()

각 명령은 수신자 객체에 대한 특정 작업을 캡슐화합니다.

실제 작업을 수행하는 수신자(receiver)를 생성합니다:

class Light:
    def turn_on(self):
        print("Light is on")
    
    def turn_off(self):
        print("Light is off")

명령을 실행하는 인보커를 생성합니다:

class RemoteControl:
    def __init__(self):
        self.command = None
    
    def set_command(self, command):
        self.command = command
    
    def press_button(self):
        self.command.execute()

커맨드 패턴을 사용하세요:

# 수신자(receiver) 생성
light = Light()

# 커맨드 생성
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

# 호출자(invoker) 생성
remote = RemoteControl()

# 다양한 커맨드 실행
remote.set_command(light_on)
remote.press_button()

remote.set_command(light_off)
remote.press_button()

실행 취소 작업에 대한 지원을 추가합니다:

class UndoableCommand(Command):
    def undo(self):
        pass

class LightOnCommand(UndoableCommand):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_on()
    
    def undo(self):
        self.light.turn_off()

class SmartRemote:
    def __init__(self):
        self.last_command = None
    
    def execute_command(self, command):
        command.execute()
        self.last_command = command
    
    def undo(self):
        if self.last_command:
            self.last_command.undo()

smart_remote = SmartRemote()
smart_remote.execute_command(LightOnCommand(light))
smart_remote.undo()  # 조명을 끕니다

출력:

Light is on
Light is off
Light is on
Light is off

핵심 포인트: 커맨드 패턴(Command Pattern)은 요청을 저장, 전달 및 나중에 실행할 수 있는 객체로 변환합니다. 호출자(invoker)는 작업을 수행하는 방법을 알 필요가 없습니다. 단지 커맨드 객체의 execute()를 호출할 뿐입니다. 이를 통해 실행 취소/다시 실행(undo/redo), 작업 큐잉(queuing operations), 커맨드 로깅(logging commands)과 같은 기능을 구현할 수 있습니다.

challenge icon

챌린지

쉬움

이 챌린지에서는 command.py에 커맨드 패턴의 필수 구성 요소인 기초 Command 베이스 클래스를 구현하게 됩니다. 이 연습은 특히 캡슐화에 초점을 맞춥니다: 데이터를 비공개로 저장하고 프로퍼티를 통해 안전하게 노출하는 방식입니다.

TODO 주석에 따라 command.py만 수정하세요. TODO 주석은 다음을 수행하도록 안내합니다:

  • 커맨드 이름을 비공개 속성(_name)으로 저장하기
  • 읽기 전용 프로퍼티(name)를 통해 노출하기
  • 커맨드 이름을 출력하는 display_info() 메서드 구현하기

참고: 이 챌린지는 베이스 Command 클래스만 다룹니다. Invoker, Receiver 및 실행 취소(undo) 기능은 이후 레슨에서 소개됩니다. 이 단계를 완료하면 전체 패턴의 기반이 되는 캡슐화 기초를 다질 수 있습니다.

구현한 내용은 다음 사항을 검증하는 driver.py에 의해 테스트됩니다:

  • 기본 기능 및 출력 형식
  • 엣지 케이스 (빈 입력, 특수 문자, 긴 이름)
  • 읽기 전용 프로퍼티 보호 (obj.name = ... 시도를 하면 AttributeError가 발생해야 함)
  • 비공개 _name 속성의 존재 여부

치트 시트

커맨드 패턴(Command Pattern)은 요청을 객체로 캡슐화하여, 작업을 큐에 저장하거나 로그를 기록하고 실행 취소(undo) 기능을 지원할 수 있게 합니다.

기본적인 커맨드 구조:

class Command:
    def execute(self):
        pass

class LightOnCommand(Command):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_on()

class LightOffCommand(Command):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_off()

실제 작업을 수행하는 수신자(receiver)를 생성합니다:

class Light:
    def turn_on(self):
        print("Light is on")
    
    def turn_off(self):
        print("Light is off")

커맨드를 실행하는 호출자(invoker)를 생성합니다:

class RemoteControl:
    def __init__(self):
        self.command = None
    
    def set_command(self, command):
        self.command = command
    
    def press_button(self):
        self.command.execute()

사용 예시:

# 수신자 생성
light = Light()

# 커맨드 생성
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

# 호출자 생성
remote = RemoteControl()

# 다양한 커맨드 실행
remote.set_command(light_on)
remote.press_button()

remote.set_command(light_off)
remote.press_button()

실행 취소(undo) 기능 추가:

class UndoableCommand(Command):
    def undo(self):
        pass

class LightOnCommand(UndoableCommand):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_on()
    
    def undo(self):
        self.light.turn_off()

class SmartRemote:
    def __init__(self):
        self.last_command = None
    
    def execute_command(self, command):
        command.execute()
        self.last_command = command
    
    def undo(self):
        if self.last_command:
            self.last_command.undo()

직접 해보기

# command.py에서 Command 클래스를 가져옵니다.
from command import Command

# 종합 테스트 케이스 처리기
test_case = input()

if test_case == "basic_test":
    obj = Command("Test Name")
    obj.display_info()
elif test_case == "validation_test":
    obj = Command("Validation Test")
    print(f"Name: {obj.name}")
elif test_case == "empty_name_test":
    obj = Command("")
    obj.display_info()
    print(f"Empty name handled: {'Yes' if obj.name == '' else 'No'}")
elif test_case == "property_access_test":
    obj = Command("Property Test")
    original_name = obj.name
    try:
        # name은 읽기 전용 속성이므로 실패해야 합니다.
        obj.name = "Modified Name"
        print("Property protection failed")
    except AttributeError:
        print("Property protected successfully")
    print(f"Name unchanged: {obj.name == original_name}")
elif test_case == "multiple_commands_test":
    commands = [
        Command("First Command"),
        Command("Second Command"),
        Command("Third Command")
    ]
    for cmd in commands:
        cmd.display_info()
elif test_case == "attribute_test":
    obj = Command("Attribute Test")
    has_private_name = hasattr(obj, "_name")
    print(f"Has _name attribute: {has_private_name}")
elif test_case == "special_chars_test":
    obj = Command("!@#$%^&*()_+{}[]|\\:;\"'<>,.?/")
    obj.display_info()
elif test_case == "long_name_test":
    long_name = "A" * 100
    obj = Command(long_name)
    obj.display_info()
    print(f"Name length: {len(obj.name)}")
quiz icon실력 점검

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

Object Oriented Programming의 모든 레슨