A A
[Design Pattern] Command Pattern - ์ปค๋งจ๋“œ ํŒจํ„ด

Command Pattern

์ปค๋งจ๋“œ ํŒจํ„ด์€ ์š”์ฒญ์„ ๋…๋ฆฝ๋œ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์š”์ฒญ์— ๋Œ€ํ•œ ๋ชจ๋“  ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ํ–‰๋™ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
  • ๋‹ค์–‘ํ•œ ์š”์ฒญ์„ ๋ฉ”์„œ๋“œ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”ํ•˜๊ณ , ์š”์ฒญ์˜ ์‹คํ–‰์„ ์ง€์—ฐํ•˜๊ฑฐ๋‚˜ ํ์— ๋„ฃ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์‹คํ–‰ ์ทจ์†Œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ ๋ช…๋ น๋“ค์„ ์ถ”์ƒํ™”ํ•ด์„œ class๋กœ ์ •์˜ํ•˜๊ณ  Object๋กœ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋Š” Pattern์ž…๋‹ˆ๋‹ค.
  • ๋˜ํ•œ ํ–‰์œ„(Behavior) ํŒจํ„ด์— ์†ํ•ฉ๋‹ˆ๋‹ค.
  • ํด๋ž˜์Šค๊ฐ€ ์ผ์„ ์‹คํ–‰ํ• ๋–„, ์ž์‹ ์˜ ํด๋ž˜์Šค or ๋‹ค๋ฅธ ํด๋ž˜์Šค์˜ Method๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
    • ๊ทธ๋Ÿฌ๋‚˜ Method๋ฅผ ํ˜ธ์ถœํ•œ ๊ฒฐ๊ณผ๋Š” Object์— ๋ฐ˜์˜๋˜์ง€๋งŒ, ์ผ์˜ ์ด๋ ฅ์€ ์–ด๋””์—๋„ ๋‚จ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์ด๋•Œ, ‘์ด ์ผ์„ ์‹คํ–‰ํ•˜์‹œ์˜ค’ ๋ผ๋Š” ‘๋ช…๋ น’์„ ํ‘œํ˜„ํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ ์žˆ์œผ๋ฉด ํŽธํ•ฉ๋‹ˆ๋‹ค.
  • ๋ช…๋ น์„ ๋‚˜ํƒ€๋‚ด๋Š” ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋กœ ํ•˜๋‚˜์˜ ‘๋ฌผ๊ฑด’์ฒ˜๋Ÿผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ช…๋ น์— ๋Œ€ํ•œ ์ด๋ ฅ์„ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ์—๋Š” ๊ทธ ์ธ์Šคํ„ด์Šค์˜ ์ง‘ํ•ฉ์„ ๊ด€๋ฆฌํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • ๋ช…๋ น์˜ ์ง‘ํ•ฉ์„ ์ €์žฅํ•ด์ฃผ๋ฉด ๊ฐ™์€ ๋ช…๋ น์„ ์žฌ์‹คํ–‰ํ•  ์ˆ˜๋„ ์žˆ๊ณ , ๋ณต์ˆ˜์˜ ๋ช…๋ น์„ ๋ชจ์•„์„œ ์ƒˆ๋กœ์šด ๋ช…๋ น์œผ๋กœ ์žฌ์ด์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Diagram

  • Command: ๋ช…๋ น์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ์—ญํ• ์ž…๋‹ˆ๋‹ค.
  • ConcreteCommand (Command1): ์‹ค์ œ๋กœ ์‹คํ–‰๋˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„, ์ฆ‰ Command๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
  • Invoker: ๋ช…๋ น์˜ ํ–‰๋™์„ ๊ฐœ์‹œํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋ฉฐ, Command์—์„œ ์ •์˜๋˜๋Š” API๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
  • Receiver: Command๊ฐ€ ๋ช…๋ น์„ ์‹คํ–‰ํ•  ๋•Œ ๊ทธ ๋Œ€์ƒ์ด ๋˜๋Š” ์—ญํ• , ๋ช…๋ น์„ ๋ฐ›์•„๋“ค์ด๋Š” ์ˆ˜์‹ ์ž๋ผ๊ณ  ์ง€์นญํ•ฉ๋‹ˆ๋‹ค.

Example

class Command:
    def execute(self):
        pass

class PrintCommand(Command):
    def __init__(self, print_str: str):
        self.print_str = print_str
    
    def execute(self):
        print(f"from print command: {self.print_str}")
  • Base Interface๋กœ execute() ๋ฉ”์„œ๋“œ๋งŒ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
  • Command๋ฅผ ์ƒ์†๋ฐ›์Šต๋‹ˆ๋‹ค.
  • __init__ ๋ฉ”์„œ๋“œ์—์„œ print_str์„ ์ธ์ž๋กœ ๋ฐ›์•„ ๋‚ด๋ถ€์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • execute() ๋ฉ”์„œ๋“œ์—์„œ ์ €์žฅ๋˜์–ด ์žˆ๋Š” print_str์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

 

first_command = PrintCommand("first command")
second_command = PrintCommand("second command")

first_command.execute()
second_command.execute()
from print command: first command
from print command: second command
  • PrintCommand ๊ฐ์ฒด์ธ first_command, second_command ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋“ค์„ ์‹คํ–‰์‹œํ‚ค๋ฉด ์˜ˆ์ƒํ•œ ๋Œ€๋กœ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

์กฐ๊ธˆ ๋” ๋ณต์žกํ•œ Command ํด๋ž˜์Šค ๊ตฌ์กฐ

  • Dog: ‘sit’,’stay’ 2๊ฐ€์ง€ ๋ช…๋ น์–ด๋ฅผ ์•Œ์•„ ๋“ฃ์Šต๋‹ˆ๋‹ค.
  • DogCommand:
    • Command์—์„œ ์ƒ์†์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.
    • Dog์—์„œ ๋ช…๋ น์„ ๋‚ด๋ฆฌ๋Š” class๋กœ ๋‚ด๋ถ€์— ๋ช…๋ น์„ ๋‚ด๋ฆด Dog Object๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
    • ๋ช…๋ น๋“ค์„ ์ €์žฅํ•˜๋Š” ๋ฆฌ์ŠคํŠธ์ธ Commands๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
from typing import List

class Dog:
    def sit(self):
        print("The dog sat down")
    
    def stay(self):
        print("The dog is staying")

class DogCommand(Command):
    def __init__(self, dog: Dog, commands: List[str]):
        self.dog = dog
        self.commands = commands

    def execute(self):
        for command in self.commands:
            if command == 'sit':
                self.dog.sit()
            elif command == 'stay':
                self.dog.stay()
  • Dog
    • sit(), stay() 2๊ฐ€์ง€ ๋ช…๋ น ํ•จ์ˆ˜๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
  • Dog Command
    • Command๋ฅผ ์ƒ์†๋ฐ›์Šต๋‹ˆ๋‹ค.
    • __init__: Dog object์™€ ๋ช…๋ น์–ด list์ธ commands๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ๋‚ด๋ถ€์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
    • execute()
      • commands ๋ช…๋ น ๋ฆฌ์ŠคํŠธ์—์„œ string ํ•˜๋‚˜์”ฉ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.
      • string์ด 'sit'์ด๋ฉด sit() ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
      • string์ด 'stay'์ด๋ฉด stay() ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
baduk = Dog()
dog_command = DogCommand(baduk, ['stay', 'sit', 'sit'])
dog_command.execute()
  • ๋ช…๋ น์„ ๋“ค์„ Dog object์ธ baduk ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • DogCommand object์ธ dog_command ์ƒ์„ฑ ํ›„ baduk๊ณผ ๋ช…๋ น์–ด list์— 'stay', 'sit', 'sit'์„ ๋„ฃ์–ด์„œ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  • ์‹คํ–‰์‹œํ‚ค๋ฉด ๊ฐ•์•„์ง€๋Š” stay๋ฅผ ํ•œ ํ›„ 2๋ฒˆ sit ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  • Command Interface๋Š” Command Class
  • Command๋ฅผ ์ƒ์†๋ฐ›๋Š” Command1์€ Dog Command Class,
  • Command1์˜ ๋ช…๋ น์„ ๋ฐ›๋Š” Object์ธ Receiver1์€ Dog์™€ ๋Œ€์‘๋ฉ๋‹ˆ๋‹ค.
์—ฌ๊ธฐ์„œ Invoker Class๋Š” ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

 

 

class Invoker:
    def __init__(self):
        self.command_list = []

    def addCommand(self, command: Command):
        self.command_list.append(command)

    def runCommands(self):
        for command in self.command_list:
            command.execute()

 

Invoker ํด๋ž˜์Šค:

  • __init__: ๋ช…๋ น๋“ค์„ ์ €์žฅํ•˜๋Š” ๋ฆฌ์ŠคํŠธ์ธ command_list๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
  • addCommand: Command ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
  • runCommands: command_list์˜ ๋ชจ๋“  command๋ฅผ execute()๋ฅผ ํ†ตํ•ด ์‹คํ–‰์‹œํ‚ค๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

 

invoker = Invoker()
invoker.addCommand(first_command)
invoker.addCommand(dog_command)
invoker.addCommand(second_command)
invoker.runCommands()
  • Invoker ๊ฐ์ฒด์ธ invoker๋ฅผ ์ƒ์„ฑํ•œ ํ›„ addCommand๋ฅผ ํ†ตํ•ด ์ด์ „์— ์ƒ์„ฑํ–ˆ๋˜ Command ๊ฐ์ฒด๋“ค์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  • runCommands๋ฅผ ํ†ตํ•ด ์‹คํ–‰ํ•˜๋ฉด ๋ชจ๋“  ๋ช…๋ น์–ด๊ฐ€ ์ฐจ๋ก€๋Œ€๋กœ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
Command Pattern์€ ๋ช…๋ น์„ ์ถ”์ƒํ™”ํ•ด์„œ ๊ฐ์ฒด๋กœ ๋‹ค๋ฃจ๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.