Команда (Command): выбор операции во время выполнения


В книге Advanced C++:Programming Styles And Idioms (Addison-Wesley, 1992), Джим Коплен (Jim Coplien) ввел термин функтор, который является объектом, главнейшим назначением которого является инкапсуляция функций (так как "функтор" имеет математическое значение, в этой книге я буду использовать более явный термин функционный объект). Назначение его состоит в отделении выбора вызываемой функции от места, где функция вызывается.

Этот термин упоминается, но не используется в книге Design Patterns. Однако, тема функционных объектов повторяется в нескольких шаблонах этой книги.

Команда (Command)- это функционный объект в чистом виде: метод, который является объектом. [7] При упаковывании метода в объект вы можете передать его в другой метод или объект в качестве параметра, чтобы попросить его выполнить определенную операцию в процессе выполнения вашего запроса. Вы можете сказать, что Command является посыльным (поскольку он предназначен и используется достаточно прямолинейно), который переносит поведение, а не данные.

//: command:CommandPattern.java
package command;

import java.util.*;

import junit.framework.*;

interface Command {
  
void execute();
}

class Hello implements Command {
  
public void execute() {
     
System.out.print("Hello ");
  
}
}

class World implements Command {
  
public void execute() {
     
System.out.print("World! ");
  
}
}

class IAm implements Command {
  
public void execute() {
     
System.out.print("I'm the command pattern!");
  
}
}

// Объект, который хранит команды:
class Macro {
  
private List commands = new ArrayList();
  
  
public void add(Command c) {
     
commands.add(c);
  
}
  
  
public void run() {
     
Iterator it = commands.iterator();
     
while (it.hasNext())
         ((
Command) it.next()).execute();
  
}
}

public class CommandPattern extends TestCase {
  
Macro macro = new Macro();
  
  
public void test() {
     
macro.add(new Hello());
      macro.add
(new World());
      macro.add
(new IAm());
      macro.run
();
  
}
  
  
public static void main(String args[]) {
     
junit.textui.TestRunner.run(CommandPattern.class);
  
}
}
// /:~

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

Другим примером Command является refractor: DirList.java [????]. Класс DirFilter является объектом команды, который содержит действие в методе accept( ), который передается в метод list( ). Метод list( ) определяет, что включать в результат, вызывая метод accept( ).

В книге Design Patterns говорится, что "Команды являются объектно-ориентированной заменой для обратного вызова [8]". Однако, я думаю, что слово "обратный" является существенной частью концепции обратных вызовов. Таким образом, я думаю, обратный вызов возвращается назад к создателю обратного вызова. С другой стороны, объект Command вы обычно создаете и передаете в некоторый метод или объект, и никак иначе не привязываетесь к объекту Command. Такой мой взгляд на этот предмет. Далее в этой книге я собрал группу шаблонов проектирования под заголовком "обратные вызовы".

Стратегия ведет себя сродни классу Command, все наследуется от одного базового класса. Но если вы присмотритесь к Command, вы увидите, что они имеют одинаковую структуру: иерархия функционных объектов. Различие в способе использования этой иерархии. Как видно из refactor:DirList.java, вы используете Command для решения определенной задачи - в данном случае, для выбора файлов из списка. То, что "остается прежним" - это тело метода, который вызывает, а часть, которая меняется, изолирована в функционном объекте. Я рискну сказать, что Command предоставляет гибкость, пока вы пишите программу, в то время, как Strategy предоставляет гибкость во время выполнения. Тем не менее, это выглядит хрупким различием.

Упражнение

  1. Используйте команду из 1-го упражнения 3 главы.