- Паттерн Command своими словами
- Где это применяется?
- Command Design Pattern
- Command Pattern
- Command Design Pattern Example
- Command Pattern Receiver Classes
- Command Pattern Interface and Implementations
- Command Pattern Invoker Class
- Command Pattern Class Diagram
- Command Pattern Important Points
- Command Design Pattern JDK Example
Паттерн 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() , который будет выполнять и который называется в терминах паттерна «команда ( command )»
/*the Command interface*/ public interface Command
Необходимо инкапсулировать выполнение умений класса 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"
Где это применяется?
- Тестовое задание: «Написать Интерпретатор на язык BrainFuck»
- Тестовое задание «Image Comparison»
- Java — быстрее, сильнее и выше! Зарплаты украинских программистов
- История успеха спустя 1.5 года от начала обучения
- Технические вопросы на собеседовании
- Как найти работу? Рассылка резюме
- Профессиональное выгорание. Как устоять?
- Английский для IT и для собеседования
- Как создать исполняемый jar в Intellij IDEA / how to create jar in IDEA
- Паттерн Singleton своими словами
- Помогите, нужна мотивация!
Command Design Pattern
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
Command Pattern is one of the Behavioral Design Pattern. Command design pattern is used to implement loose coupling in a request-response model.
Command Pattern
In command pattern, the request is send to the invoker and invoker pass it to the encapsulated command object. Command object passes the request to the appropriate method of Receiver to perform the specific action. The client program create the receiver object and then attach it to the Command. Then it creates the invoker object and attach the command object to perform an action. Now when client program executes the action, it’s processed based on the command and receiver object.
Command Design Pattern Example
We will look at a real life scenario where we can implement Command pattern. Let’s say we want to provide a File System utility with methods to open, write and close file. This file system utility should support multiple operating systems such as Windows and Unix. To implement our File System utility, first of all we need to create the receiver classes that will actually do all the work. Since we code in terms of interface in java, we can have FileSystemReceiver interface and it’s implementation classes for different operating system flavors such as Windows, Unix, Solaris etc.
Command Pattern Receiver Classes
package com.journaldev.design.command; public interface FileSystemReceiver
FileSystemReceiver interface defines the contract for the implementation classes. For simplicity, I am creating two flavors of receiver classes to work with Unix and Windows systems.
package com.journaldev.design.command; public class UnixFileSystemReceiver implements FileSystemReceiver < @Override public void openFile() < System.out.println("Opening file in unix OS"); >@Override public void writeFile() < System.out.println("Writing file in unix OS"); >@Override public void closeFile() < System.out.println("Closing file in unix OS"); >>
package com.journaldev.design.command; public class WindowsFileSystemReceiver implements FileSystemReceiver < @Override public void openFile() < System.out.println("Opening file in Windows OS"); >@Override public void writeFile() < System.out.println("Writing file in Windows OS"); >@Override public void closeFile() < System.out.println("Closing file in Windows OS"); >>
Did you noticed the Override annotation and if you wonder why it’s used, please read java annotations and override annotation benefits. Now that our receiver classes are ready, we can move to implement our Command classes.
Command Pattern Interface and Implementations
We can use interface or abstract class to create our base Command, it’s a design decision and depends on your requirement. We are going with interface because we don’t have any default implementations.
package com.journaldev.design.command; public interface Command
Now we need to create implementations for all the different types of action performed by the receiver. Since we have three actions we will create three Command implementations. Each Command implementation will forward the request to the appropriate method of receiver.
package com.journaldev.design.command; public class OpenFileCommand implements Command < private FileSystemReceiver fileSystem; public OpenFileCommand(FileSystemReceiver fs)< this.fileSystem=fs; >@Override public void execute() < //open command is forwarding request to openFile method this.fileSystem.openFile(); >>
package com.journaldev.design.command; public class CloseFileCommand implements Command < private FileSystemReceiver fileSystem; public CloseFileCommand(FileSystemReceiver fs)< this.fileSystem=fs; >@Override public void execute() < this.fileSystem.closeFile(); >>
package com.journaldev.design.command; public class WriteFileCommand implements Command < private FileSystemReceiver fileSystem; public WriteFileCommand(FileSystemReceiver fs)< this.fileSystem=fs; >@Override public void execute() < this.fileSystem.writeFile(); >>
Now we have receiver and command implementations ready, so we can move to implement the invoker class.
Command Pattern Invoker Class
Invoker is a simple class that encapsulates the Command and passes the request to the command object to process it.
package com.journaldev.design.command; public class FileInvoker < public Command command; public FileInvoker(Command c)< this.command=c; >public void execute() < this.command.execute(); >>
Our file system utility implementation is ready and we can move to write a simple command pattern client program. But before that I will provide a utility method to create the appropriate FileSystemReceiver object. Since we can use System class to get the operating system information, we will use this or else we can use Factory pattern to return appropriate type based on the input.
package com.journaldev.design.command; public class FileSystemReceiverUtil < public static FileSystemReceiver getUnderlyingFileSystem()< String osName = System.getProperty("os.name"); System.out.println("Underlying OS is:"+osName); if(osName.contains("Windows"))< return new WindowsFileSystemReceiver(); >else < return new UnixFileSystemReceiver(); >> >
Let’s move now to create our command pattern example client program that will consume our file system utility.
package com.journaldev.design.command; public class FileSystemClient < public static void main(String[] args) < //Creating the receiver object FileSystemReceiver fs = FileSystemReceiverUtil.getUnderlyingFileSystem(); //creating command and associating with receiver OpenFileCommand openFileCommand = new OpenFileCommand(fs); //Creating invoker and associating with Command FileInvoker file = new FileInvoker(openFileCommand); //perform action on invoker object file.execute(); WriteFileCommand writeFileCommand = new WriteFileCommand(fs); file = new FileInvoker(writeFileCommand); file.execute(); CloseFileCommand closeFileCommand = new CloseFileCommand(fs); file = new FileInvoker(closeFileCommand); file.execute(); >>
Notice that client is responsible to create the appropriate type of command object. For example if you want to write a file you are not supposed to create CloseFileCommand object. Client program is also responsible to attach receiver to the command and then command to the invoker class. Output of the above command pattern example program is:
Underlying OS is:Mac OS X Opening file in unix OS Writing file in unix OS Closing file in unix OS
Command Pattern Class Diagram
Here is the class diagram for our file system utility implementation.
Command Pattern Important Points
- Command is the core of command design pattern that defines the contract for implementation.
- Receiver implementation is separate from command implementation.
- Command implementation classes chose the method to invoke on receiver object, for every method in receiver there will be a command implementation. It works as a bridge between receiver and action methods.
- Invoker class just forward the request from client to the command object.
- Client is responsible to instantiate appropriate command and receiver implementation and then associate them together.
- Client is also responsible for instantiating invoker object and associating command object with it and execute the action method.
- Command design pattern is easily extendible, we can add new action methods in receivers and create new Command implementations without changing the client code.
- The drawback with Command design pattern is that the code gets huge and confusing with high number of action methods and because of so many associations.
Command Design Pattern JDK Example
Runnable interface (java.lang.Runnable) and Swing Action (javax.swing.Action) uses command pattern.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.