A classe Command


A classe Command é uma classe abstracta que define a abstração utilizada na framework po-uilib para representar uma 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 a 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. Assim, 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. O primeiro argumento representa o formulário que está associado ao comando e o segundo o Display que pode ser utilizado para apresentar dados ao utilizador. Assim, não é 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. Cada vez que um comando é executado pela framework, o método execute() é invocado sobre o objecto comando correspondente. Antes da execução deste método, o formulário do comando é realizado e caso haja perguntas associadas ao formulário, estas serão feitas ao utilizador. Se o formulário estiver vazio, então não será feita qualquer pergunta. Isto evita alguma duplicação de código (invocação do método parse() sobre o formulário no método execute() do comando) e evita o erro de esquecimento da invocação do método parse() antes de tentar aceder a uma ddada resposta a uma pergunta do formulário. Antes da invocação do método execute() sobre o comando em causa, é ainda esvaziado o display que lhe está associado por forma a que a mensagem que foi construída e apresentada na execução anterior do comando não apareça outra vez. 

Relativamente à construção do formulário  associado a cada comando, a classe Command define o seguinte conjunto de métodos que deve ser utilizado para construir o formulário associado ao comando, normalmente feito no construtor da subclasse:
  • public void addBooleanField(String key, String prompt)
  • public void addRealField(String key, String prompt)
  • public void addIntegerField(String key, String prompt)
  • public void addStringField(String key, String prompt)
Estes métodos têm a funcionalidade indicada para os métodos com o mesmo nome para a entidade Form aplicada ao formulário associado ao comando.
 Para aceder às respostas dos pedidos indicados no formulário do comando, a classe Command oferece os seguintes métodos:
  • public Boolean booleanField(String key)
  • public Integer integerField(String key)
  • public Double realField(String key)
  • public String stringField(String key)
Estes métodos têm uma funcionalidade semelhante aos métodos com o mesmo nome de Form aplicada ao formulárop associado ao comando. Normalmente, só é necessário obter as respostas indicadas pelo utilizador na concretização do método execute() da subclasse Command em causa.
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 inicializá-la  no construtor da classe derivada, então a superclasse 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.

Mostrar um menu


A operação de apresentar um menu é uma operação que é muito utilizada. Esta operação consiste em ter definir um comando que é responsável por criar um menu e depois apresentá-lo quando o comando é selecionado pelo utilizador. Dado que este tipo de operação é muito recorrente, criou-se uma classe derivada de Command que tem esta funcionalidade com o objectivo de facilirar a concretização desta funcionalide. Esta nova classe derivada, DoOpenMenu, está associada a um menu, que corresponde ao menu a apresentar quando o utilizador seleciona este comando. O construtor desta classe recebe dois argumentos. O primeiro é a etiqueta do comando e o segundo é o menu a apresentar.

Visibilidade de comandos


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 funcionalidade 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.

Último comando


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;
  • public Command(boolean last, String title) - cria um comando sem receiver (ou com receiver igual a null).


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 o construtor deve inicializar o formulário do comando utilizando os métodos disponíveis de Command necessário para criar os vários pedidos de informação que devem ser associados ao formulário.

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.uilib.menus.Command;
import pt.tecnico.uilib.menus.CommandException; public class DoSomething extends Command<MyReceiver> { //Constructor public DoSomething(MyReceiver ent) { super("Título deste Comando", ent); addIntegerField("number", "Introduza um número: "); addStringField("string", "Introduza uma cadeia de caracteres: "); } ... }

A classe derivada tem que substituir o método protected void execute() throws CommandException. Este método concretiza a funcionalidade que o comando deve oferecer e de um modo geral segue o seguinte procedimento. Não é necessário pedir os dados ao utilizador relativos ao formulário do comando porque isso é feito de forma automática pela framework antes da execução do método execute() sobre o comando em causa. Assim, pode-se utilizar logo os métodos de acesso aos pedidos do formulário para se obter as respostas do utilizador. Para executar a funcionaldiade requerida será necessário invocar uma ou mais operações sobre uma ou mais entidades do domínio da aplicação em que a entidade _receiver do comando representa a entidade do domínio que sabe fazer a funcionaldiade requerida ou é o ponto de entrada no grafo de entidades do domínio para se chegar à entidade que sabe realizar a funcionaldiade requerida. Finalmente, 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
    String res = _receiver.doSomething(integerField("number"), stringField("string"))
    _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);