Padrão de Projeto Command

Padrão de Comando


“`

No padrão de comando, a solicitação é enviada ao invocador e este a repassa ao objeto de comando encapsulado. O objeto de comando passa a solicitação para o método apropriado do objeto de receptor para realizar a ação específica. O programa cliente cria o objeto de receptor e o anexa ao comando. Em seguida, ele cria o objeto invocador e anexa o objeto de comando para executar uma ação. Agora, quando o programa cliente executa a ação, ela é processada com base no comando e no objeto de receptor.

“`plaintext

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 queremos fornecer uma utilidade de sistema de arquivos com métodos para abrir, escrever e fechar arquivos. Esta utilidade de sistema de arquivos deve suportar múltiplos 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 simplificar, estou criando dois sabores 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 Override e se está se perguntando por que ela é usada, por favor, leia sobre anotações Java e os benefícios da anotação de sobreposição. Agora que nossas classes receptoras estão prontas, podemos passar para a implementação de nossas classes de comando.

Interface e Implementações do Padrão de Comando

Podemos usar interface ou classe abstrata para criar nosso Comando base, essa é uma decisão de design e depende dos requisitos. Vamos optar por uma 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ção realizados pelo receptor. Como temos três ações, criaremos três implementações de Comando. Cada implementação de Comando encaminhará a solicitação 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 a solicitação 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 de comando prontas, podemos passar para a implementação da classe invocadora.

Classe Invocadora do Padrão de Comando

O Invocador é uma classe simples que encapsula o Comando e passa a solicitação para o objeto de comando processá-la.

package com.journaldev.design.command;

public class FileInvoker {

	public Command command;
	
	public FileInvoker(Command c){
		this.command=c;
	}
	
	public void execute(){
		this.command.execute();
	}
}

Nosso implementação de utilitário de sistema de arquivos está pronta e podemos avançar para escrever um programa cliente simples usando o padrão de comando. Mas antes disso, vou fornecer um método de utilitário para criar o objeto apropriado FileSystemReceiver. Como podemos usar a classe System para obter informações do sistema operacional, vamos usar isso; caso contrário, podemos usar o padrão Factory 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();
		 }
	}
	
}

Vamos agora criar nosso programa cliente de exemplo usando o padrão de comando que consumirá nosso utilitário 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 o comando e associando com o receptor
		OpenFileCommand openFileCommand = new OpenFileCommand(fs);
		
		// Criando o invocador e associando com o comando
		FileInvoker file = new FileInvoker(openFileCommand);
		
		// Executando a 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 acima usando o padrão de comando é:

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 da nossa utilidade de sistema de arquivos.

Pontos Importantes do Padrão de Comando

  • O Command é o núcleo do padrão de design de comando que define o contrato para a implementação.
  • A implementação do Receiver é separada da implementação do Command.
  • As classes de implementação do Command escolhem o método a ser invocado no objeto Receiver; para cada método no Receiver, haverá uma implementação do Command. Isso funciona como uma ponte entre o Receiver e os métodos de ação.
  • A classe Invoker apenas encaminha a solicitação do cliente para o objeto Command.
  • O Cliente é responsável por instanciar o Command apropriado e a implementação do Receiver, e depois associá-los entre si.
  • O Cliente também é responsável por instanciar o objeto Invoker e associar o objeto Command a ele, e executar o método de ação.
  • O padrão de design Command é facilmente extensível; podemos adicionar novos métodos de ação nos Receivers e criar novas implementações do Command sem alterar o código do cliente.
  • A desvantagem do padrão de design Command é 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.

Padrão de Design Command Exemplo JDK

Interface Runnable (java.lang.Runnable) e Swing Action (javax.swing.Action) utilizam o padrão de comando.

Source:
https://www.digitalocean.com/community/tutorials/command-design-pattern