A Classe Command


A classe Command é uma classe abstracta que representa uma dada funcionalidade da aplicação que se quer disponibilizar ao utilizador. Esta classe define o método abstracto protected void execute() que representa a funcionalidade a ser executada por cada comando. Cada subclasse de Command deve substituir este método com o código específico da funcionalidade que deve ser concretizada pelo comando em causa. 

Do ponto de vista da framework po-uilib existe uma ligação entre a classe Menu e a classe Command. Cada opção de um menu é concretizada por uma subclasse de Command. Por esta razão cada comando tem um título (é um dos parâmetros indicados na construção de um comando). Quando o menu apresenta as suas opções ao utilizador, a cadeia de caracteres que o menu utiliza para descrever cada opção do menu é o título do comando correspondente à opção.
A classe Command suporta um determinado conjunto de funcionalidades que normalmente é necessário em todas as subclasse, diminuindo assim o custo de concretização de cada opção. A ideia é refactorizar a funcionalidade comum aos vários comandos na superclasse por forma evitar duplicação de código e tornar assim o desenvolvimento das subclasses de Command mais rápido. Uma das refactorizações realizada teve em conta que normalmente a funcionalidade de um comando exige que haja interacção com o utilizador, seja para pedir ou apresentar dados,  a classe Command tem dois atributos, um do tipo Form (guardado no atributo _form) e outro do tipo Display (guardado no atributo _display). Estes dois atributos são inicializados automaticamente no processo de construção de um comando. Assim, qualquer subclasse de Command pode aceder directamente as estes dois atributos para pedir ou apresentar dados ao utilizador, não sendo necessário criar uma instância de Display ou de Form sempre que uma subclasse de Command precise de apresentar ou pedir dados ao utilizador.

Uma segunda refactorição realizada tirou partido fo facto que normalmente, cada comando está ainda associado uma entidade (designada como receiver) que é a entidade que realmente sabe realizar a funcionalidade do comando em causa. Normalmente esta entidade é uma entidade da camda da lógica de negócio. Esta entidade é iindicada quando se cria um comando e é depois utilizada sempre que o comando é executado (via invocação do método execute()). Por forma a evitar estar a declarar um atributo em cada classe derivada de Command para guardar a entidade receiver associada ao comando e iniciazá-la  no construtor da classe derivada, então a superclass Command já tem um atributo para guardar a entidade receiver (com o nome _receiver). Dado que o tipo desta entidade varia de aplicação para aplicação (e pode inclusivamente variar dentro da mesma aplicação) então a classe Command é uma classe genérica em que o tipo paramétrico utilizado representa o tipo da entidade receiver associada ao comando. Assim, na especificação da classe derivada de Command é necessário indicar qual o tipo da entidade receiver quando se indica a superclasse (... extends Command<TipoDoReceiver>). Dado que o atributo _receiver de Command tem o nível de acesso protected isto quer dizer que qualquer classe derivada de Command pode aceder directamente a este atributo.

Por vezes acontece que quando se escolhe uma dada opção do menu já não se quer apresentar novamente a lista de de opções e pretende-se sim terminar o processamento do meu em causa. Por exemplo, se tivessemos uma aplicação editor gráfico que tinham um menu com uma opção para criar um quadrado, outra para criar um círculo e outra para apresentar o menu de edição sobre uma forma gráfica. Se este menu de edição incluisse a opção de remover a forma, então não faria sentido continuar no menu de edição da forma seleccionada após se ter escolhido a opção de remover a forma dado que a forma já foi removida do editor. Neste caso, o comando que concretizase esta opção deveria ser classificado como último comando

Esta característica dos comandos é representada pela propriedade ser o último comando. O valor desta propriedade é indicado no momento de criação de um comando através do construtor da superclasse Command utilizado. Do ponto de vista de um menu, a forma como esta funcionalidade é suportada é a seguinte. Quando um menu é aberto, ele fica em ciclo até o utilizador escolher a opção 0 ou então até ser executado um comando que tenha esta propriedade com o valor verdadeiro.

A classe Command oferece os seguintes construtores:
  • public Command(String title, Receiver receiver) - cria um comando com o título e receiver indicados e cuja propriedade ser último comando é falsa;
  • public Command(boolean last, String title, Receiver receiver) - semelhante ao anterior mas o valor da propriedade ser último comando é dado pelo parâmetro last do construtor.

A concretização mais comum de um comando corresponde a criar uma subclasse de Command que normalmente terá apenas dois métodos: o construtor e o método execute(). Normalmente o construtor recebe um único parâmetro, a entidade receiver, e invoca o construtor correcto da super classe, indicando pelo menos o títutlo do comando e a entidade receiver (se existir). Se o comando só puder ser executado uma única vez (ou seja, é um último comando), então dever-se-á utilizar o 2º construtor da superclasse e utilizar true no primeiro parâmetro. Caso o comando precise de pedir dados ao utilizador, então é necessário ter um atributo na classe derivada por cada dado que se quer pedir ao utilizador e inicializar esses atributos no construtor através do métodos disponíveis do Form já associado ao comando (e guardado no atributo _form).

Por exemplo, um comando que utilize um receiver do tipo MyReceiver e precise de pedir um inteiro e uma cadeia de caracteres ao utilizador deverá ter a seguinte estrutura e construtor:

import myapp.core.MyReceiver;
import pt.tecnico.po.ui.Command;
import pt.tecnico.po.ui.DialogException;
import pt.tecnico.po.ui.Input;

public class DoSomething extends Command<MyReceiver> {
        
    /** Number to read. */
    private Input<Integer> _number;

    /** String to read. */
    private Input<String> _string;

    //Constructor
    public DoSomething(MyReceiver ent) {
        super("Título deste Comando", ent);
        _number = _form.addIntegerInput("Introduza um número: ");
        _string    = _form.addStringInput("Introduza uma cadeia de caracteres: ");
    }

    ...
}

A classe derivada tem que substituir o método protected void execute() throws DialogException. Este método concretiza a funcionalidade que o comando deve oferecer e de um modo geral segue o seguinte procedimento. Primeiro deve realizar os pedidos de dados ao utilizador invocando o método parse() sobre o formulário associado ao comando (guardado no atributo _form). Depois de ter invocado o método parse() já é possível saber os valores que o utilizador prencheu no formulário invocando o método value() sobre cada pedido de dados associado ao formulário. De seguida acede-se ao objecto receiver para executar uma determinada operação  e depois, caso seja necessário apresentar dados ao utilizador, constroi-se a mensagem a apresentar ao utilizador utilizando os métodos add() ou addLine() sobre o Display associado ao comando (guardado no atributo _display) e quando a mensagem já está totalmente construída apresenta-se a mensagem ao utilizador invocando o método display() sobre o objecto Display.

Considerando o exemplo anterior, e supondo que a concretização da funcionalidade deste comando consiste em invocar o método doSomething sobre o receiver do comando com os valores introduzidos pelo utilizador e apresentando ao utilizador a cadeia de caracteres devolvido pela execução do método doSomething, então o método execute deste comando deveria ter o seguinte código:

protected void execute() {  // executed when this option is selected
    _form.parse();

    String res = _receiver.doSomething(_number.value(), _string.value())
    _display.addLine(res);
    _display.display();
}

Note-se que neste caso as duas invocações ao objecto referenciado por _display poderiam ser substituídas por uma só:_display.popup(res);

Finalmente, um comando pode ou não ser visísel. Um comando que seja visível é sempre apresentado quando se listam as opções de um menu. Um comando que seja invisível não é listado como uma opção do menu nem pode ser escolhido. Esta funcioanlidade deve ser utilizada quando só fizer sentido disponibilizar uma dada opção em determinadas situações. Esta funcionalidade é descrita de forma detalhada na seguinte secção.