Padrão Comportamental: Padrão de Comando

Introdução ao Padrão de Comando: Em comparação com outros padrões de projeto, o padrão de comando não possui tantas restrições, não sendo um padrão muito "rígido". Por essa razão, o padrão de comando é mais flexível em relação a outros padrões de design. Um exemplo comum do padrão de comando são os comandos de menu, como em sistemas operacionais, onde ao clicar no comando "Desligar", o sistema executa uma série de operações: primeiro, pausa o processamento de eventos, salva algumas configurações do sistema, encerra os processos dos programas e, finalmente, invoca o comando do kernel para desligar o computador. O padrão de comando funciona de maneira semelhante.

Definição do Padrão de Comando: Encapsular uma solicitação como um objeto, permitindo que diferentes solicitações sejam parametrizadas para o cliente, além de possibilitar enfileirar solicitações ou registrar logs de operações.

Cenários de Uso do Padrão de Comando: Quando é necessário abstrair ações a serem executadas e fornecê-las como parâmetros - semelhante a callbacks em programação procedural, sendo o padrão de comando a implementação orientada a objetos desses callbacks.

O padrão de comando foca principalmente na relação entre o solicitante do comando e seu executor, onde o comando contém uma referência ao executor.

A seguir, usaremos o jogo "Tetris" como exemplo para explicar a aplicação do padrão de comando.

No jogo Tetris, existem 4 botões: dois para movimentação lateral (esquerda e direita), um para queda rápida e outro para transformar a forma da peça. Um jogador corresponde ao nosso cliente, enquanto os 4 botões do jogo correspondem a 4 solicitantes ou invocadores. A lógica específica de execução dos comandos dos botões pode ser considerada o papel do comando.

A seguir, ilustraremos com código. Primeiro, o receptor do comando:

1 /**
2  * Receptor do comando, máquina do jogo Tetris
3  * 
4  */
5 public class MaquinaTetris {
6 
7     // Lógica real para processar a operação "mover para a esquerda"
8     public void moverEsquerda(){
9         System.out.println("Movendo para a esquerda");
10     }
11 
12     // Lógica real para processar a operação "mover para a direita"
13     public void moverDireita(){
14         System.out.println("Movendo para a direita");
15     }
16 
17     // Lógica real para processar a operação "queda rápida"
18     public void quedaRapida(){
19         System.out.println("Queda rápida");
20     }
21 
22     // Lógica real para processar a operação "transformar forma"
23     public void transformar(){
24         System.out.println("Transformando forma");
25     }
26 }

A classe MaquinaTetris é o único lugar no padrão de comando que processa a lógica específica do código. Todas as outras classes chamam direta ou indiretamente os métodos dessa classe, que é o papel do receptor - processar a lógica específica.

Em seguida, definimos uma interface de comando como abstração para o papel do comando:

1 /**
2  * Abstração para o papel do comando
3  */
4 public interface Comando {
5     // Método de execução do comando
6     void executar();
7 }

Agora, implementamos os 4 comandos:

 1 /**
 2  * Comando para mover para a esquerda
 3  */
 4 public class ComandoEsquerda implements Comando{
 5     // Mantém uma referência ao executor do comando
 6     MaquinaTetris maquinaTetris;
 7 
 8     public void setMaquinaTetris(MaquinaTetris maquinaTetris){
 9         this.maquinaTetris = maquinaTetris;
10     }
11 
12     @Override
13     public void executar() {
14         // Chama o método da máquina de jogo para execução específica
15         maquinaTetris.moverEsquerda();
16     }
17 }

 1 /**
 2  * Comando para mover para a direita
 3  */
 4 public class ComandoDireita implements Comando{
 5     // Mantém uma referência ao executor do comando
 6     MaquinaTetris maquinaTetris;
 7 
 8     public void setMaquinaTetris(MaquinaTetris maquinaTetris){
 9         this.maquinaTetris = maquinaTetris;
10     }
11 
12     @Override
13     public void executar() {
14         // Chama o método da máquina de jogo para execução específica
15         maquinaTetris.moverDireita();
16     }
17 }

 1 /**
 2  * Comando para queda rápida
 3  */
 4 public class ComandoQuedaRapida implements Comando{
 5     // Mantém uma referência ao executor do comando
 6     MaquinaTetris maquinaTetris;
 7 
 8     public void setMaquinaTetris(MaquinaTetris maquinaTetris){
 9         this.maquinaTetris = maquinaTetris;
10     }
11 
12     @Override
13     public void executar() {
14         // Chama o método da máquina de jogo para execução específica
15         maquinaTetris.quedaRapida();
16     }
17 }

 1 /**
 2  * Comando para transformar forma
 3  */
 4 public class ComandoTransformar implements Comando{
 5     // Mantém uma referência ao executor do comando
 6     MaquinaTetris maquinaTetris;
 7 
 8     public void setMaquinaTetris(MaquinaTetris maquinaTetris){
 9         this.maquinaTetris = maquinaTetris;
10     }
11 
12     @Override
13     public void executar() {
14         // Chama o método da máquina de jogo para execução específica
15         maquinaTetris.transformar();
16     }
17 }

Acima temos os 4 comandos correspondentes. Agora implementamos um solicitante de comando:

 1 /**
 2  *
 3  * Solicitante do comando
 4  */
 5 public class Controles {
 6     // Comando para mover para a esquerda
 7     private ComandoEsquerda comandoEsquerda;
 8     // Comando para mover para a direita
 9     private ComandoDireita comandoDireita;
10     // Comando para queda rápida
11     private ComandoQuedaRapida comandoQuedaRapida;
12     // Comando para transformar forma
13     private ComandoTransformar comandoTransformar;
14 
15     // Define o comando para mover para a esquerda
16     public void setComandoEsquerda(ComandoEsquerda comandoEsquerda) {
17         this.comandoEsquerda = comandoEsquerda;
18     }
19 
20     // Define o comando para mover para a direita
21     public void setComandoDireita(ComandoDireita comandoDireita) {
22         this.comandoDireita = comandoDireita;
23     }
24 
25     // Define o comando para queda rápida
26     public void setComandoQuedaRapida(ComandoQuedaRapida comandoQuedaRapida) {
27         this.comandoQuedaRapida = comandoQuedaRapida;
28     }
29 
30     // Define o comando para transformar forma
31     public void setComandoTransformar(ComandoTransformar comandoTransformar) {
32         this.comandoTransformar = comandoTransformar;
33     }
34 
35     // Pressionar botão para mover para a esquerda
36     public void pressionarEsquerda(){
37         comandoEsquerda.executar();
38     }
39 
40     // Pressionar botão para mover para a direita
41     public void pressionarDireita(){
42         comandoDireita.executar();
43     }
44 
45     // Pressionar botão para queda rápida
46     public void pressionarQueda(){
47         comandoQuedaRapida.executar();
48     }
49 
50     // Pressionar botão para transformar forma
51     public void pressionarTransformar(){
52         comandoTransformar.executar();
53     }
54 
55 }

Finalmente, o cliente decide como usar:

Classe cliente:

 1 /**
 2  * Jogador, também o cliente
 3  */
 4 public class Jogador {
 5     public static void main(String[] args){
 6         testarPadrao();
 7     }
 8 
 9     // Testar o padrão de comando
10     private static void testarPadrao() {
11         // Jogo Tetris
12         MaquinaTetris maquinaTetris = new MaquinaTetris();
13 
14         // Com base no jogo, criamos 4 tipos de comando
15         ComandoEsquerda comandoEsquerda = new ComandoEsquerda();
16         ComandoDireita comandoDireita = new ComandoDireita();
17         ComandoQuedaRapida comandoQuedaRapida = new ComandoQuedaRapida();
18         ComandoTransformar comandoTransformar = new ComandoTransformar();
19 
20         // Referência ao executor específico de cada comando
21         comandoEsquerda.setMaquinaTetris(maquinaTetris);
22         comandoDireita.setMaquinaTetris(maquinaTetris);
23         comandoQuedaRapida.setMaquinaTetris(maquinaTetris);
24         comandoTransformar.setMaquinaTetris(maquinaTetris);
25 
26         // Os controles podem executar diferentes comandos
27         Controles controles = new Controles();
28         controles.setComandoEsquerda(comandoEsquerda);
29         controles.setComandoDireita(comandoDireita);
30         controles.setComandoQuedaRapida(comandoQuedaRapida);
31         controles.setComandoTransformar(comandoTransformar);
32 
33         // Qual botão pressionar é decisão do jogador
34         controles.pressionarEsquerda();
35         controles.pressionarDireita();
36         controles.pressionarQueda();
37         controles.pressionarTransformar();
38     }
39 
40     // Para a maioria dos desenvolvedores, é mais fácil aceitar o código abaixo
41     private static void metodoAlternativo(){
42         // Jogo Tetris
43         MaquinaTetris maquina = new MaquinaTetris();
44 
45         // Para implementar o controle desejado, basta chamar a função correspondente
46         maquina.moverEsquerda();
47         maquina.moverDireita();
48         maquina.quedaRapida();
49         maquina.transformar();
50     }
51 
52 
53 }

Após ler todo esse código, você pode estar se perguntando: por que um problema tão simples precisa ser tratado de forma tão complexa? Para a maioria dos desenvolvedores, seria mais aceitável usar o código do métodoAlternativo(), como:

    // Para a maioria dos desenvolvedores, é mais fácil aceitar o código abaixo
41     private static void metodoAlternativo(){
42         // Jogo Tetris
43         MaquinaTetris maquina = new MaquinaTetris();
44 
45         // Para implementar o controle desejado, basta chamar a função correspondente
46         maquina.moverEsquerda();
47         maquina.moverDireita();
48         maquina.quedaRapida();
49         maquina.transformar();
50     }

A lógica é tão complexa porque facilita o desenvolvimento. Cada vez que precisamos adicionar ou modificar recursos do jogo, só precisamos modificar a classe MaquinaTetris e, em seguida, ajustar a classe Jogador. Torna tudo muito conveniente. No entanto, o que é conveniente para o desenvolvedor pode não ser para quem vier depois. Um princípio importante dos padrões de projeto é: "fechado para modificação, aberto para extensão". Pode refletir sobre isso.

Tags: padrão de projeto comportamental comando java tetris

Publicado em 7-7 16:34