Статьи

Шаблон проектирования команд в Java

В этом уроке мы узнаем о шаблоне команд, который является важным шаблоном проектирования поведения. Он имеет несколько важных приложений, таких как реализация функций отмены / повтора в текстовых редакторах.

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

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

Почему шаблон проектирования команд?

Есть несколько популярных вариантов использования шаблона команды:

  • Хранение и планирование запросов: мы можем представлять наши запросы как объекты команд, которые затем могут быть сохранены в списках, обработаны, поставлены в очередь или завершены в определенное время с помощью триггера событий. Например, функциональность звонка тревоги может быть достигнута путем представления его в качестве объекта команды и выполнения его в триггере события.
  • Поддерживает Done / Undone: шаблон команды позволяет нам выполнять или отменять операции, выполняемые командой

Поскольку каждый объект команды поддерживает операцию do / undo, мы можем расширить эту функциональность для разработки операции do / undo для текстового редактора. Идея состоит в том, чтобы иметь два списка объектов команд — историю и список повторов :

  • Список истории содержит все команды, которые были выполнены до сих пор
  • Список повторов , с другой стороны, хранит команды, которые мы отменили
  • Для каждого запроса команды объект команды создается, выполняется и затем добавляется в список истории.
  • По запросу отмены мы проверим и вызовем операцию отмены самой последней команды в списке истории, а затем поместим эту команду в список восстановления
  • Для операции повтора мы возьмем самую последнюю команду из списка повторов , ту, которую мы недавно отменили, а затем выполним ее и снова переместим в список истории.

Звучит просто верно!

Представление UML:

Мы можем представить шаблон проектирования команды как:

Где мы имеем,

  • Команда: интерфейс или абстрактный класс, определяющий операции для объектов команды.
  • ConcreteCommand: это конкретные классы, которые содержат фактическую реализацию для конкретной команды
  • Receiver: класс команды вызывает получателя для выполнения запрошенной операции
  • Invoker: класс, который открыт для клиента. Ответственный за вызов соответствующей команды

Более того, каждый класс команд обычно обеспечивает реализацию этих методов:

  • execute (): определяет работу, которая должна быть сделана
  • uninecute (): этот метод отвечает за отмену операции
  • isReversible (): этот метод должен возвращать true, если команда может быть отменена, иначе false

Пример реализации:

Предположим, мы должны реализовать функцию вырезания-копирования-вставки для текстового редактора.

Итак, начнем с определения нашего интерфейса Command :

1
2
3
4
5
6
7
8
9
public interface Command {
  
    void execute();
    void unexecute();
  
    default boolean isReversible() {
        return true;
    }
}

Также предположим, что у нас есть класс Document, поддерживающий вставку и удаление текста:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
//class which will be our Receiver
public class Document {
  
    public void insert(String str, int position) {
        ...
    }
  
    public String delete(int position, int noOfChars) {
        ...
    }
  
    public void copy(int position, int noOfChars) {
        ...
    }
}

Написание конкретных команд:

Теперь мы определим наш класс CutCommand :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public class CutCommand implements Command {
  
    private Document doc;
    private String text;
    private int startPosition;
    private int noOfChars;
  
    //suitable constructor
  
    public void execute() {
        this.text = this.doc.delete(startPosition, noOfChars);
    }
  
    public void unexecute() {
        this.doc.insert(text, startPosition);
    }
}

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class CopyCommand implements Command {
  
    private Document doc;
    private int startPosition;
    private int length;
  
    //suitable constructor
  
    public void execute() {
        this.doc.copy(startPosition, length);
    }
  
    public void unexecute() {
        System.out.println("Uncopy operation is blocked");
    }
  
    public boolean isReversible() { return false; }
}
  
public class PasteCommand implements Command {
  
    private Document doc;
    private String text;
    private int startPosition;
  
    //suitable constructor
  
    public void execute() {
        this.doc.insert(text, startPosition);
    }
  
    public void unexecute() {
        this.doc.delete(startPosition, text.length());
    }
}

Как мы знаем, uncopy не является допустимой операцией, мы вернули false в нашем методе isReversible () класса CopyCommand .

Реализация Invoker:

Наконец, мы можем написать класс invoker:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class DocumentInvoker {
  
    private Document document;
    private CommandManager commandManager;
  
    public DocumentInvoker(Document document) {
        this.document = document;
        commandManager = CommandManage.getInstance();
    }
  
    public void cut(int position, int length) {
        Command cutCommand = new CutCommand(document, position, length);
        commandManager.invoke(cutCommand);
    }
  
    public void copy(int position, int length) {
        Command copyCommand = new CopyCommand(document, position, length);
        commandManager.invoke(copyCommand);
    }
  
   public void paste(String text, int position) {
        Command pasteCommand = new PasteCommand(document, text, position);
        commandManager.invoke(pasteCommand);
    }
  
}

Здесь CommandManager — это класс, который управляет историей и списками повторов . Вызывающий объект создает экземпляр объекта команды с необходимой ему информацией, а затем вызывает диспетчер команд, чтобы, наконец, выполнить операцию.

Вывод:

В этом уроке мы узнали, как реализовать шаблон проектирования команд в Java.

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

Опубликовано на Java Code Geeks с разрешения Шубхры Шриваставы, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Шаблон проектирования команд в Java

Мнения, высказанные участниками Java Code Geeks, являются их собственными.