Menu
Coddy logo textTech

커맨드 패턴

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

Command Pattern은 요청을 객체로 캡슐화하여 작업을 대기열에 추가하고, 요청을 기록하며, undo 기능을 지원할 수 있도록 합니다. 이 패턴은 작업을 호출하는 객체와 작업을 수행하는 객체를 분리합니다.

다음은 간단한 command 클래스입니다:

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()

각 Command는 receiver 객체에 대한 특정 작업을 캡슐화합니다.

실제 작업을 수행하는 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()

command 패턴을 사용하세요:

# 리시버 생성
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()

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는 작업을 수행하는 방법을 알 필요가 없으며, 단지 command 객체에서 execute()를 호출하기만 하면 됩니다. 이를 통해 undo/redo, 작업 대기열 추가, commands 로깅과 같은 기능을 사용할 수 있습니다.

challenge icon

챌린지

쉬움

이 챌린지에서는 Command Pattern의 핵심 구성 요소인 기초 Command 기본 클래스command.py에 구현합니다. 이 연습은 특히 캡슐화에 중점을 둡니다. 즉, 데이터를 private하게 저장하고 property를 통해 안전하게 노출하는 것입니다.

TODO 주석에 따라 command.py만 수정하세요. TODO 주석은 다음 작업을 안내합니다.

  • 명령 이름을 private attribute(_name)로 저장하기
  • read-only property(name)를 통해 이름 노출하기
  • 명령 이름을 출력하는 display_info() method 구현하기

참고: 이 챌린지에서는 기본 Command 클래스만 다룹니다. Invoker, Receiver 및 undo 기능은 이후 레슨에서 소개됩니다. 이 단계를 완료하면 전체 패턴의 기반이 되는 캡슐화의 토대를 마련할 수 있습니다.

여러분의 구현은 다음 항목을 검증하는 driver.py에 의해 테스트됩니다.

  • 기본 기능 및 출력 형식
  • 엣지 케이스(빈 입력, 특수 문자, 긴 이름)
  • read-only property 보호(obj.name = ...을 시도하면 AttributeError가 발생해야 함)
  • private _name attribute의 존재 여부

직접 해보기

# 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실력 점검

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

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

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