Padrão de Comando é um dos Padrões de Design Comportamental. O padrão de design de comando é usado para implementar o acoplamento flexível em um modelo de solicitação e resposta.
Padrão de Comando
No padrão de comando, a solicitação é enviada ao
invoker
e o invoker a repassa para o objeto de command
encapsulado. O objeto de comando passa a solicitação para o método apropriado do Receiver
para realizar a ação específica. O programa cliente cria o objeto receptor e o anexa ao comando. Em seguida, ele cria o objeto invoker e anexa o objeto de comando para realizar uma ação. Agora, quando o programa cliente executa a ação, ela é processada com base no comando e no objeto receptor.
Exemplo de Padrão de Design de Comando
Vamos analisar um cenário da vida real onde podemos implementar o padrão de Comando. Digamos que desejamos fornecer uma utilidade de Sistema de Arquivos com métodos para abrir, escrever e fechar arquivos. Esta utilidade de sistema de arquivos deve suportar vários sistemas operacionais, como Windows e Unix. Para implementar nossa utilidade de Sistema de Arquivos, primeiro precisamos criar as classes receptoras que realmente farão todo o trabalho. Como codificamos em termos de interface em java, podemos ter a interface FileSystemReceiver
e suas classes de implementação para diferentes sabores de sistemas operacionais, como Windows, Unix, Solaris etc.
Classes Receptoras do Padrão de Comando
package com.journaldev.design.command;
public interface FileSystemReceiver {
void openFile();
void writeFile();
void closeFile();
}
A interface FileSystemReceiver define o contrato para as classes de implementação. Para simplicidade, estou criando duas variantes de classes receptoras para trabalhar com sistemas Unix e Windows.
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");
}
}
Você notou a anotação de Override e se está se perguntando por que ela é usada, por favor, leia anotações em java e benefícios da anotação de override. Agora que nossas classes receptoras estão prontas, podemos passar para a implementação de nossas classes de Comando.
Interface de Padrão de Comando e Implementações
Podemos usar interface ou classe abstrata para criar nosso Comando base, é uma decisão de design e depende dos seus requisitos. Estamos optando por interface porque não temos implementações padrão.
package com.journaldev.design.command;
public interface Command {
void execute();
}
Agora precisamos criar implementações para todos os diferentes tipos de ações executadas pelo receptor. Como temos três ações, criaremos três implementações de Comando. Cada implementação de Comando encaminhará o pedido para o método apropriado do receptor.
package com.journaldev.design.command;
public class OpenFileCommand implements Command {
private FileSystemReceiver fileSystem;
public OpenFileCommand(FileSystemReceiver fs){
this.fileSystem=fs;
}
@Override
public void execute() {
// O comando de abertura está encaminhando o pedido para o método openFile
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();
}
}
Agora que temos o receptor e as implementações do comando prontas, podemos passar para a implementação da classe invocadora.
Classe Invocadora do Padrão de Comando
A Invocadora é uma classe simples que encapsula o Comando e passa o pedido para o objeto de comando para processá-lo.
package com.journaldev.design.command;
public class FileInvoker {
public Command command;
public FileInvoker(Command c){
this.command=c;
}
public void execute(){
this.command.execute();
}
}
Nossa implementação de utilidade do sistema de arquivos está pronta e podemos avançar para escrever um programa cliente de padrão de comando simples. Mas antes disso, fornecerei um método de utilidade para criar o objeto FileSystemReceiver
apropriado. Como podemos usar a classe Sistema para obter informações do sistema operacional, usaremos isso ou então podemos usar o padrão de fábrica para retornar o tipo apropriado com base na entrada.
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();
}
}
}
Agora vamos criar nosso programa cliente de exemplo de padrão de comando que consumirá nossa utilidade de sistema de arquivos.
package com.journaldev.design.command;
public class FileSystemClient {
public static void main(String[] args) {
//Criando o objeto receptor
FileSystemReceiver fs = FileSystemReceiverUtil.getUnderlyingFileSystem();
//criando comando e associando com receptor
OpenFileCommand openFileCommand = new OpenFileCommand(fs);
//Criando invocador e associando com o comando
FileInvoker file = new FileInvoker(openFileCommand);
//realizando ação no objeto invocador
file.execute();
WriteFileCommand writeFileCommand = new WriteFileCommand(fs);
file = new FileInvoker(writeFileCommand);
file.execute();
CloseFileCommand closeFileCommand = new CloseFileCommand(fs);
file = new FileInvoker(closeFileCommand);
file.execute();
}
}
Observe que o cliente é responsável por criar o tipo apropriado de objeto de comando. Por exemplo, se você quiser escrever um arquivo, não deve criar o objeto CloseFileCommand
. O programa cliente também é responsável por associar o receptor ao comando e, em seguida, o comando à classe invocadora. A saída do programa de exemplo de padrão de comando acima é:
Underlying OS is:Mac OS X
Opening file in unix OS
Writing file in unix OS
Closing file in unix OS
Diagrama de Classe do Padrão de Comando
Aqui está o diagrama de classes para a implementação de nossa utilidade de sistema de arquivos.
Pontos Importantes do Padrão de Comando
- O comando é o núcleo do padrão de design de comando que define o contrato para a implementação.
- A implementação do receptor é separada da implementação do comando.
- As classes de implementação do comando escolhem o método a ser invocado no objeto receptor; para cada método no receptor, haverá uma implementação de comando. Funciona como uma ponte entre o receptor e os métodos de ação.
- A classe Invoker simplesmente encaminha a solicitação do cliente para o objeto comando.
- O cliente é responsável por instanciar o comando apropriado e a implementação do receptor e, em seguida, associá-los.
- O cliente também é responsável por instanciar o objeto invoker e associar o objeto comando a ele, e executar o método de ação.
- O padrão de design de comando é facilmente extensível; podemos adicionar novos métodos de ação nos receptores e criar novas implementações de comando sem alterar o código do cliente.
- A desvantagem do padrão de design de comando é que o código fica extenso e confuso com um grande número de métodos de ação e devido a tantas associações.
Exemplo de Padrão de Projeto de Comando em JDK
A interface Runnable (java.lang.Runnable) e a Ação Swing (javax.swing.Action) utilizam o padrão de comando.
Source:
https://www.digitalocean.com/community/tutorials/command-design-pattern