Паттерн Command своими словами.

Привет всем, Форумчане!


Разобрался-таки основательно(как мне кажется) с паттером Command и хочу попытаться рассказать о нем «своими словами».
Исходя из википедии, можем узнать, что цель его
Создание структуры, в которой класс-отправитель и класс-получатель не зависят друг от друга напрямую. Организация обратного вызова к классу, который включает в себя класс-отправитель.

В принципи написано чётко и правильно, но это в теории. А как же сделать это?
Вот этим начинаются проблемы, т.к. описание уже не так ясно и очевидно.
Поэтому я как разобрался, решил рассказать вам как я это понял, может кому-то и пригодится:

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

В этом паттерне есть четыре термина, пока примем их как данность: команды(command), приемник команд(receiver), вызывающий команды(invoker) и клиент(client).

Пример буду брать с той же википедии, он вполне сносный.

Задача
есть класс Light, который умеет две вещи: включить свет и выключить. Он в терминах паттерна будет «приемник команд(receiver)»
/*Receiver class*/

public class Light{
     public Light(){  }
   
     public void turnOn(){
        System.out.println("The light is on");
     }

     public void turnOff(){
        System.out.println("The light is off");
     }
}


Создадим интерфейс с одним методом execute(), который будет выполнять и который называется в терминах паттерна «команда(commadn)»
/*the Command interface*/

public interface Command{
    void execute();
}


Необходимо инкапсулировать выполнение умений класса Light. Для этого мы создадим классы TurnOnLightCommand и TurnOffLightCommand, которые реализуют интерфейс Command и которые будут принимать в конструкторе объект класса Light. И каждый из них будет выполнять только одно действие. Один будет вызывать метод turnOn(), а другой turnOff().
/*the Command for turning on the light*/

public class TurnOnLightCommand implements Command{
   private Light theLight;

   public TurnOnLightCommand(Light light){
        this.theLight=light;
       }

   public void execute(){
      theLight.turnOn();
   }
}

/*the Command for turning off the light*/

public class TurnOffLightCommand implements Command{
   private Light theLight;

   public TurnOffLightCommand(Light light){
        this.theLight=light;
       }

   public void execute(){
      theLight.turnOff();
   }
}


Теперь пришло время создать объект, который бы принимал эти инкапсулированные методы объекта Light. Он в терминах паттерна называется вызывающий команды (invoker). Назовем его Switch и пусть он будет принимать в конструкторе переменные Command, которые будут использоваться в созданных методах flipUp() и flipDown().
/*the Invoker class*/

public class Switch {
    private Command flipUpCommand;
    private Command flipDownCommand;

    public Switch(Command flipUpCommand,Command flipDownCommand){
         this.flipUpCommand=flipUpCommand;
         this.flipDownCommand=flipDownCommand;
    }

    public void flipUp(){
         flipUpCommand.execute();
    }
    
    public void flipDown(){
         flipDownCommand.execute();
    }
}


Ну и конечно создадим класс который будет использовать их, чтобы понять что происходит вообще. Он будет именть метод main, в котором и будет происходить всё действие:
/*The test class*/
public class TestCommand{
   public static void main(String[] args){
       // создаем объект, который будет использоваться
       Light l=new Light();
       // создаем объекты для всех умений объекта Light:
       Command switchUp=new TurnOnLightCommand(l);
       Command switchDown=new TurnOffLightCommand(l);
        
       // Создаемтся invoker, с которым мы будем непосредственно контактировать:
       Switch s=new Switch(switchUp,switchDown);
       
       // вот проверка этого, используем методы:
       s.flipUp();
       s.flipDown();
   }
}

На выводе будет следующее:

"The light is on"
"The light is off"


Где это применяется?
По цели ясно, что и для чего это нужно, а именно: в ситуации, когда нужно отделить конкретное исполнение, это очень удобно. Чтоб использование каких-то функций не зависело от конкретной реализации и ее можно было изменять без ущерба для системы.
как-то так...)
Пишите свои комментарии, давайте обсуждать, может что-то можно сделать проще и рассказать лучше, будем всё править, если необходимо) Чтоб для тех, кто будет читать первый раз, было как можно понятнее.

Ну и кому понравится статья — ставьет "+" на ней :) Для меня это важно)


Со временем хочу написать еще про Builder, Singleton и прочие.


См. также мои другие статьи:
Тестовое задание: «Написать Интерпретатор на язык BrainFuck»
Тестовое задание «Image Comparison»
Java — быстрее, сильнее и выше! Зарплаты украинских программистов.
История успеха спустя 1.5 года от начала обучения
Технические вопросы на собеседовании.
Как найти работу? Рассылка резюме
Профессиональное выгорание. Как устоять?
Английский для IT и для собеседования
Паттерн Command своими словами.
Паттерн Singleton своими словами.
Как создать исполняемый jar в Intellij IDEA / how to create jar in IDEA
Помогите, нужна мотивация!

Подписывайтесь на мой блог Паттерны Проектирования пишите в нем статьи!

4 комментария

Torin
Я все понял, только не понял зачем мне это нужно. Давай еще примеров! примеры наше все! :) А лучше описать проблему, которую мог бы решить этот паттерн
Roman_kh
Вот нашел пример, понравился мне:

public class Steps {
    public void goSouth() {
        System.out.println("step to south");
    }
    
    public void goNorth() {
        System.out.println("step to north");
    }
    
    public void goEast() {
        System.out.println("step to east");
    }
    
    public void goWest() {
        System.out.println("step to west");
    }
}

public abstract class StepsCommand implements Command {
    protected Steps steps = new Steps();
}

public interface Command {
    void execute();
}

public class GoEastCommand extends StepsCommand {
    @Override
    public void execute() {
        steps.goEast();
    }
}

public class GoNorthCommand extends StepsCommand {
    @Override
    public void execute() {
        steps.goNorth();
    }
}

public class GoSouthCommand extends StepsCommand {
    @Override
    public void execute() {
        steps.goSouth();
    }
}

public class GoWestCommand extends StepsCommand {
    @Override
    public void execute() {
        steps.goWest();
    }
}

public class Navigator {
    private final List<StepsCommand> steps = new LinkedList<>();
    private final List<StepsCommand> path = new LinkedList<>();
    
    public Navigator registerStep(StepsCommand step) {
        steps.add(step);
        return this;
    }
    
    public void go() {
        for(StepsCommand step : steps) {
            step.execute();
            ((LinkedList)path).addFirst(step);
        }
        steps.clear();
    }
    
    public void goBack() {
        for(StepsCommand step : path) {
            step.execute();
        }
        path.clear();
    }
}

public class Client {
    public static void main(String[] args) {
        Navigator navigator = 
            new Navigator().registerStep(new GoEastCommand())
                           .registerStep(new GoNorthCommand())
                           .registerStep(new GoNorthCommand())
                           .registerStep(new GoSouthCommand());
        System.out.println("go");
        navigator.go();
        System.out.println("go back");
        navigator.goBack();
    }
}
/**
 * Output:
 * go
 * step to east
 * step to north
 * step to north
 * step to south
 * go back
 * step to south
 * step to north
 * step to north
 * step to east
 */
imp
  • imp
  • +3
2Роман
насколько я понял этот паттерн у тебя еще должен быть CommandExecutor(единственная точка входа)
то есть к самим командам у тебя не должен быть доступа.

соот-но команды ты должен вызывать CommandExecutor.execute(Команда)
Nullptr
Вот тут довольно хороший и понятный пример разбора паттерна Команда
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.