이 글은 헤드 퍼스트 디자인 패턴을 읽고 정리한 것입니다.
커맨트 패턴은 객체 지향 디자인 패턴을 통해 요청을 하는 객체와 요청을 받고 실행하는 객체를 분리한 패턴으로 요청자(invoker)와 수신자(receiver)가 decoupling 되어 있다.
setCommand()를 실행한다.execute()만 노출 시킨다. execute() 메소드가 실행 되었다는 것만 안다.
예시
리모컨으로 불을 켜는 것에 대한 책의 예시를 파이썬으로 작성 했다.
from abc import *
# The example from the Head First Design Pattern
"""
Receiver: Some appliances
"""
class Light:
def on(self):
print("on")
def off(self):
print("off")
"""
Command: on, off
"""
class Command(metaclass=ABCMeta):
@abstractmethod
def execute(self):
raise NotImplementedError()
class LightOnCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self) -> None:
self.light.on()
"""
Invoker: RemoteControl
"""
class RemoteControl:
def __init__(self, command: Command):
self.slot = command # A kind of setter in Java
def button_pressed(self) -> None:
self.slot.execute()
# Usage
class RemoteControlTest:
@classmethod
def run(cls):
light = Light()
light_on = LightOnCommand(light=light)
remote = RemoteControl(command=light_on)
remote.button_pressed() # on
execute() 실행시 리스트에 들어 있는 커맨드들이 하나씩 실행되도록 만들면 된다.예시
책의 예시를 파이썬으로 바꾸어 작성해 보았다.
# The example from the Head First Design Pattern
"""
Receiver: Some appliances
"""
from abc import ABCMeta, abstractmethod
from typing import List
class Light:
def on(self, type: str):
on_by_types = {
"living_room": "living_room_light_on",
"bath_room": "living_room_light_on",
}
print(on_by_types.get(type))
def off(self, type: str):
on_by_types = {
"living_room": "living_room_light_off",
"bath_room": "living_room_light_off",
}
print(on_by_types.get(type))
class Stereo:
def on(self):
print("on")
def off(self):
print("off")
def set_cd(self):
print("CD in")
"""
Command: on, off
"""
class Command(metaclass=ABCMeta):
@abstractmethod
def execute(self):
raise NotImplementedError()
class LightOnCommand(Command):
"""Light"""
def __init__(self, light: Light, type: str):
self.light = light
self.type = type
def execute(self) -> None:
self.light.on(self.type)
class LightOffCommand(Command):
"""Light"""
def __init__(self, light: Light, type: str):
self.light = light
self.type = type
def execute(self) -> None:
self.light.off(self.type)
class StereoOnWithCDCommand(Command):
"""Stereo"""
def __init__(self, stereo: Stereo):
self.stereo = stereo
def execute(self) -> None:
self.stereo.on()
self.stereo.set_cd()
"""
Invoker: RemoteControl
"""
class RemoteControl:
def __init__(self, on_commands: List[Command], off_commands: List[Command]):
self.on_commands = on_commands # A kind of setter in Java
self.off_commands = off_commands # A kind of setter in Java
def on_button_pressed(self, slot: int) -> None:
self.on_commands[slot].execute()
def off_button_pressed(self, slot: int) -> None:
self.off_commands[slot].execute()
# Usage
class RemoteLoader:
@classmethod
def run(cls):
light = Light()
stereo = Stereo()
on_commands = [
LightOnCommand(light=light, type="living_room"),
StereoOnWithCDCommand(stereo=stereo),
]
off_commands = [
LightOffCommand(light=light, type="living_room"),
StereoOnWithCDCommand(stereo=stereo),
]
remote = RemoteControl(on_commands=on_commands, off_commands=off_commands)
length_of_commands = len(on_commands)
for slot in range(length_of_commands):
remote.on_button_pressed(slot=slot)
execute() 메소드가 불리기 이전의 상태로 되돌리면 된다.execute()만 실행하면 되며, 이 메소드는 각각의 이벤트에 대한 커맨트를 실행한다.