Шаблоны проектирования Python — шаблон команды

задняя часть Python Шаблоны проектирования API

командный режим

题目:Теперь сделайте пульт дистанционного управления умным домом, функция показана на рисунке ниже.

智能家居遥控器

На картинке ниже показаны классы, предоставляемые производителями бытовой техники, интерфейсы разные, и в будущем таких классов может быть все больше и больше.

家电厂商类

Глядя на классы, предоставляемые производителями, вы обнаружите, что многие классы предоставляют методы on(), off(), кроме того, есть такие методы, как dim(), setTemperature(), setVolumn(), setDirection(). Исходя из этого, мы можем представить, что в будущем будет больше классов поставщиков, и каждый класс будет иметь множество методов.

Если мы используем эти классы в коде удаленного управления, в коде будет много операторов if, таких как

if slot1 == Light:
    light.on()
elif slot1 == Hottub:
    hottob.jetsOn()

А если серьезно, каждый раз, когда добавляется новый класс производителя, код пульта дистанционного управления должен быть изменен соответствующим образом.

В это время мы будем动作的请求者(遥控器)от动作的执行者(厂商类)Развязка в объектах.

Как добиться развязки?

мы можем использовать命令对象. Используйте командный объект для инкапсуляции запроса (например, включение света) в определенный объект. Итак, если вы храните командный объект для каждой кнопки, вы можете попросить объект команды выполнить связанную работу, когда кнопка нажата. В это время пульт дистанционного управления не нужно знать, каково содержание работы, до тех пор, пока есть командной объект, который может взаимодействовать с правильным объектом и делать вещи хорошо.

Возьмем операцию заказа в ресторане, чтобы представить командный режим.

Рестораны обычно работают так:

  1. Клиенты заказывают еду и передают заказ официанту
  2. Официант принял заказ и передал его повару.
  3. После того, как повар получает заказ, он готовит еду в соответствии с заказом.

Здесь мы представляем себе порядок как объект, используемый для запроса приготовления еды,

  • Как и обычные объекты, объект заказа можно передавать: от официанта к счетчику заказов, интерфейс заказа содержит только один метод orderUp(). Этот метод инкапсулирует действия, необходимые для приготовления еды.
  • Работа официанта состоит в том, чтобы принять заказ, а затем вызвать метод заказа orderUp(), ему не нужно знать, что это за заказ.
  • Шеф-повар — это объект, который умеет готовить еду и является реальным исполнителем задачи.

Если рассматривать ресторан как модель шаблонов объектно-ориентированного проектирования, эта модель позволяет разделить «объекты, делающие запросы» и «объекты, получающие и выполняющие эти запросы». Например, для API удаленного управления мы хотим разделить «код кнопки, которая делает запрос» и «специфический для поставщика объект, который выполняет запрос».

回到命令模式Преобразуем блок-схему работы ресторана в блок-схему командного режима: здесь клиент соответствует клиенту на предыдущей картинке, команда соответствует заказу, вызывающий соответствует официанту, а получатель соответствует повар.

командный режим

Давайте сначала посмотрим на определение командного режима:

命令模式Инкапсулируйте «запросы» в объекты, чтобы другие объекты можно было параметризовать с помощью других запросов, очередей или журналов. Командный режим также поддерживает отмену операций.

Мы знаем, что, определяя верхнюю сторону, командный объект, связывая набор действий на конкретном получателе для запроса пакета. Чтобы добиться этого, объект команды к действию и получателю пакета в объект. Этот объект подвергается воздействию только одного метода выполнения (), когда этот метод называется, приемник будет выполнять эти действия.

Диаграмма классов командного режима выглядит следующим образом:

命令模式类图

Вернемся к дизайну пульта: мы планируем сопоставить каждый слот пульта с командой, чтобы пульт стал调用者. Когда кнопка нажата, вызывается метод execute() соответствующего командного объекта, и в результате вызывается действие приемника (например, освещение, вентиляторы, динамики).

Командный режим также поддерживает отмену, которая предоставляет метод undo(), противоположный методу execute(). Независимо от того, что делает execute(), undo() делает обратное.

Код

Реализация дистанционного управления

class RemoteControl(object):

    def __init__(self):
        # 遥控器要处理7个开与关的命令
        self.on_commands = [NoCommand() for i in range(7)] 
        self.off_commands = [NoCommand() for i in range(7)]
        self.undo_command = None  # 将前一个命令记录在这里

    def set_command(self, slot, on_command, off_command):
        # 预先给每个插槽设置一个空命令的命令
        # set_command 命令必须要有三个参数(插槽的位置、开的命令、关的命令)
        self.on_commands[slot] = on_command
        self.off_commands[slot] = off_command

    def on_button_was_pressed(self, slot):
        command = self.on_commands[slot]
        command.execute()
        self.undo_command = command
        
    # 当按下开或关的按钮,硬件就会负责调用对应的方法
    def off_button_was_pressed(self, slot):
        command = self.off_commands[slot]
        command.execute()
        self.undo_command = command

    def undo_button_was_pressed(self):
        self.undo_command.undo()

    def __str__(self):
        # 这里负责打印每个插槽和它对应的命令
        for i in range(7):
            print('[slot %d] %s %s' % (i,
                                       self.on_commands[i].__class__.__name__,
                                       self.off_commands[i].__class__.__name__))
        return ''

Реализация команды

Здесь реализован базовый класс.У этого базового класса есть два метода, execute и undo.Команда инкапсулирует набор действий определенного класса производителя.Удаленное управление может выполнить эти действия, вызвав метод execute(), или использовать undo( ) способ отменить эти действия:

class Command(object):

    def execute(self):
        # 每个需要子类实现的方法都会抛出NotImplementedError
        # 这样的话,这个类就是真正的抽象基类
        raise NotImplementedError()

    def undo(self):
        raise NotImplementedError()


# 在遥控器中,我们不想每次都检查是否某个插槽都加载了命令,
# 所以我们给每个插槽预先设定一个NoCommand 对象
# 所以没有被明确指定命令的插槽,其命令将是默认的 NoCommand 对象
class NoCommand(Command):

    def execute(self):
        print('Command Not Found')

    def undo(self):
        print('Command Not Found')

Ниже приведен класс лампочки, использующий базовый класс Command, каждое действие реализовано как простой объект команды. Объект команды содержит ссылку на экземпляр класса поставщика и реализует функцию execute(). Этот метод вызывает один или несколько методов, реализованных классом поставщика, для выполнения определенного поведения.В этом примере есть два класса: включить свет и выключить свет.

class Light(object):

    def __init__(self, name):
        # 因为电灯包括 living room light 和 kitchen light
        self.name = name

    def on(self):
        print('%s Light is On' % self.name)

    def off(self):
        print('%s Light is Off' % self.name)


# 电灯打开的开关类
class LightOnCommand(Command):

    def __init__(self, light):
        self.light = light

    def execute(self):
        self.light.on()

    def undo(self):
        # undo 是关闭电灯
        self.light.off()

        
class LightOffCommand(Command):

    def __init__(self, light):
        self.light = light

    def execute(self):
        self.light.off()

    def undo(self):
        self.light.on()

Выполните код, здесь создаются несколько объектов команд, а затем загружаются в слоты пульта. Каждый объект команды инкапсулирует запрос на автоматизацию устройства:

def remote_control_test():
    remote = RemoteControl()

    living_room_light = Light('Living Room')
    kitchen_light = Light('Kitchen')

    living_room_light_on = LightOnCommand(living_room_light)
    living_room_light_off = LightOffCommand(living_room_light)
    kitchen_light_on = LightOnCommand(kitchen_light)
    kitchen_light_off = LightOffCommand(kitchen_light)

    remote.set_command(0, living_room_light_on, living_room_light_off)
    remote.set_command(1, kitchen_light_on, kitchen_light_off)

    print(remote)

    remote.on_button_was_pressed(0)
    remote.off_button_was_pressed(0)
    remote.undo_button_was_pressed()
    remote.on_button_was_pressed(1)
    remote.off_button_was_pressed(1)
    remote.undo_button_was_pressed()

Вывод после выполнения:

[slot 0] LightOnCommand LightOffCommand
[slot 1] LightOnCommand LightOffCommand
[slot 2] NoCommand NoCommand
[slot 3] NoCommand NoCommand
[slot 4] NoCommand NoCommand
[slot 5] NoCommand NoCommand
[slot 6] NoCommand NoCommand

Living Room Light is On
Living Room Light is Off
Living Room Light is On
Kitchen Light is On
Kitchen Light is Off
Kitchen Light is On

Соберите несколько команд

Обычно мы также хотим иметь переключатель для включения всех огней одной клавишей, а затем для выключения всех огней одной клавишей, здесь мы используемMacroCommand:

class MacroCommand(Command):

    def __init__(self, commands):
        # 首先创建一个 commands 的 list,这里可以存放多个命令
        self.commands = commands

    def execute(self):
        # 执行时,依次执行多个开关
        for command in self.commands:
            command.execute()

    def undo(self):
        # 撤销时,给所有命令执行 undo 操作
        for command in self.commands:
            command.undo()

Коллекция тестовых переключателей:

def remote_control_test():
    remote = RemoteControl()
    
    living_room_light = Light('Living Room')
    kitchen_light = Light('Kitchen')
    garage_door = GarageDoor()

    living_room_light_on = LightOnCommand(living_room_light)
    living_room_light_off = LightOffCommand(living_room_light)
    kitchen_light_on = LightOnCommand(kitchen_light)
    kitchen_light_off = LightOffCommand(kitchen_light)

    garage_door_open = GarageDoorOpenCommand(garage_door)
    garage_door_close = GarageDoorCloseCommand(garage_door)
    
    # 测试开关集合
    party_on_macro = MacroCommand([living_room_light_on, kitchen_light_on])
    party_off_macro = MacroCommand([living_room_light_off, kitchen_light_off])
    remote.set_command(3, party_on_macro, party_off_macro)
    print('--pushing macro on--')
    remote.on_button_was_pressed(3)
    print('--pushing macro off--')
    remote.off_button_was_pressed(3)
    print('--push macro undo--')
    remote.undo_button_was_pressed()

Конечно, мы также можем использовать список для записи записей команд для достижения многоуровневых операций отмены.

Назначение командного режима

1. Запрос очереди

Команды могут упаковывать блок операций (получатель и набор действий) и передавать его как обычные объекты. Операции можно вызывать даже спустя много времени после создания объекта команды. Мы можем использовать эти функции для создания некоторых приложений, таких как планирование, пулы потоков, рабочие очереди и т. д.

想象一个工作队列:Вы добавляете команды на одном конце и потоки на другом. Поток выполняет следующие действия: удаляет команду из очереди, вызывает ее метод execute(), ждет завершения вызова, затем отбрасывает вторичный объект команды, а затем принимает следующую команду.

В это время рабочая очередь и вычисляемый объект полностью развязаны, в этот момент поток может выполнять транскодирование звука, а следующей командой может стать подсчет пользовательского балла.

2. Запрос журнала

Некоторые приложения требуют, чтобы мы регистрировали все действия и могли вспомнить эти действия, чтобы восстановить предыдущее состояние после сбоя системы. Командный режим поддерживает это, добавляя два новых метода (store(), load()). Эти данные лучше всего сохранять на жесткий диск.

要怎么做呢?Когда мы выполняем команды, мы сохраняем историю на диск, а когда система выходит из строя, мы перезагружаем объекты команд и вызываем методы execute() этих объектов последовательно в пакетах.

Например, для Excel мы могли бы захотеть реализовать восстановление после ошибок, регистрируя операции электронной таблицы в журнале, а не регистрировать всю электронную таблицу каждый раз, когда она изменяется. Транзакции базы данных также используют эту технику, то есть должна быть выполнена целая куча операций или вообще не должно выполняться никаких операций.

Ссылка на ссылку

Полный код командного режима


Наконец, поблагодарите мою подругу за ее поддержку.

Добро пожаловать в подписку (April_Louisa) купи мне фанту
欢迎关注
请我喝芬达