Comandos do menu principal
Os comandos são responsáveis pela concretização das várias opções oferecidas pelos vários menus da aplicação. Essencialmente, cada comando pede dados aos utilizador (se necessário), realiza pedidos a uma ou mais entidades do domínio por forma a realizar a funcionalidade do comando em causa e apresenta o resultado ao utilizador (caso seja necessário). As operações de pedir e apresentar dados ao utilizador são realizadas através da framework po-uilib. Nesta secção apresentamos apenas alguns dos comandos. Os restantes comandos estão disponíveis no código disponibilizado com a concretização desta aplicação exemplo.
Comando Listar os números inseridos
Este comando é responsável por apresentar todos os números guardados pela aplicação. A série de números é mantida pela classe Sequence da camada de lógica de negócio. Desta forma, a entidade receiver que deve estar associada a este comando é do tipo Sequence. A associação entre instâncias deste comando e o seu receiver é estabelecida no processo de criação de um comando pelo que o construtor desta classe deve receber a Sequence a associar como parâmetro. Dado que este comando não pede dados ao utilizador, então não é necessário personalizar o formulário por omissão deste comando. É ainda de salientar que o construtor desta classe não invoca o construtor usual da super classe Command (que recebe apenas dois argumento, o título do comando e a entidade do domínio que vai estar associada ao comando, designada como receiver), utilizando outro que tem um terceiro argumento adicional. Este argumento permite a especificação da visibilidade do comando a criar, ou seja se o comando deve ser apresentado ou não quando o menu apresenta as suas opções ao utilizador. Neste caso concreto, o comando criado só é visível caso a entidade receiver do comando tenha números. Se a entidade Sequence não tiver nenhum número associado, então este comando não será visível quando o menu for apresentado.
A concretização da funcionalidade deste comando é realizada no método execute(): primeiro, pede a lista de números inseridos pelo utilizador à entidade do domínio da aplicação que mantém a série de números inseridos pelo utilizador. Esta entidade é acedida através do atributo _receiver. Depois, constroi-se incrementalmente o texto a apresentar ao utilizador adicionando cada número da série numa linha separada através da invocação do método addLine sobre a instância de Display que está disponível para qualquer comando via atributo _display. Após se ter construído todo o texto, indica-se ao Display referenciado por _display para finalmente apresentar o texto final ao utilizador invocando o método display(). Esta invocação do método display() não é obrigatório fazer, dado que a framework po-uilib já invoca este mesmo método sobre o Display por omissão do comando quando a execução comando termina. A invocação do método display() é obrigatória quando a apresentação dos dados ao utilizador tem que ser realizada de forma incremental e não num único passo como neste caso.
package ex.app.main;
// In Command sub-classes we need to implement:
//
// 1. The constructor, that typically receives one or more objects of the core.
// We have to call the constructor of the super-class (usually with a title and the entity of the core)
// The constructor, usually, creates the several Input entities needed to read input data from the user.
// 2. The method execute() calls the methods of the core entities
//
// User interaction (input and output data) is done using the classes Form and Display as shown
// Each command has, by default, a Form and a Display that can be used in the execute method of the command,
// or you can create those objects explicitly inside the execute method.
import java.util.List;
import ex.core.Number;
import ex.core.Sequence;
import ex.app.Label;
import pt.tecnico.uilib.menus.Command;
import java.util.function.Predicate;
public class ShowSequence extends Command<Sequence> {
public ShowSequence(Sequence ent) {
super(Label.SHOW_SEQUENCE_TITLE, ent, x -> { return !ent.sequence().isEmpty();});
}
protected void execute() { // executed when this option is selected
List<Number> list = _receiver.sequence();
for (Number number : list)
_display.addLine(number.toString());
_display.display(); // not needed. It's done by default.
}
}
Comando Apresentar o menu de edição
Este comando é responsável por apresentar o menu de edição. Assim, a concretização do método execute() é muito simples: apenas tem que criar uma instância de EditMenu (a classe que define o menu de edição) e abrir o menu através da invocação do método open() sobre o menu criado.
package ex.app.main;
import ex.core.Sequence;
import ex.app.edit.EditMenu;
import pt.tecnico.uilib.menus.Command;
public class ShowEditMenu extends Command<Sequence> {
//Constructor
public ShowEditMenu(Sequence ent) {
super(Label.EDIT_MENU_TITLE, ent);
}
protected void execute() { // executed when this option is selected
EditMenu menu = new EditMenu(_receiver);
menu.open();
}
}
Esta funcionalidade pode ser concretizada de uma forma mais simples utilizando o comando DoOpenMenu já fornecedido pela framework, o qual concretiza um comando que corresponde a abrir um dado menu. Neste caso, a criação do menu principal deve criar uma instância de DoOpenMenu em vez de ShowEditMenu (que deixa de ser necessário e pode ser removido). Nem sempre é possível utilizar o comando DoOpenMenu. Esta possibilidade apenas pode ser utilizada quando o menu a abrir está sempre relacionado com a mesma entidade do domínio. Caso a entidade do domínio possa ser diferente (por exemplo, um dos outros comandos do menu exterior pode alterar esta entidade), então a solução será criar um comando semelhante a ShowEditMenu, em que o menu é criado cada vez que o comando é executado e podemos então associá-lo a uma entidade distinta do domínio.
Comando Adicionar um número
Dado que apenas é necessário ler um inteiro, a forma mais rápida e eficiente de fazer isto é utilizar o formulário por omissão do comando e adicionar um campo (do tipo inteiro) ao formulário. A adicção do campo deve ser feita apenas uma vez, portanto terá que ser feita no construtor do comando e utilizando o método addIntegerField. Este método recebe dois argumentos, um identificador e a mensagem a apresentar ao utilizador quando ele for preencher este campo. O identificador server para identificar o campo em causa no formulário. Utilizando o formulário por omissão já não é necessário invocar o método parse() sobre o formulário (para pedir ao utilizador para preencher o formulário) dentro do método execute() , dado que isso é feito de forma automática pela framework po-uilib antes da execução do método execute().
Tal como no caso dos títulos dos menus e dos comandos (que estavam definidos numa interface), as mensagens a apresentar ao utilizador relativamente a todos os comandos estão também definidas em duas interfaces, chamadas Message e Prompt, em vez de estarem espalhadas pelos vários comandos. Message contém as mensagens utilizadas para apresentar resultados ao utilizador, enquanto que Prompt contém as mensagens referentes aos vários campos dos formulários.
package ex.app.edit;
/**
* This command adds a new number to the sequence of numbers kept by the application.
**/
import ex.core.Sequence;
import ex.app.Label;
import ex.app.Prompt;
import ex.app.Message;
import pt.tecnico.uilib.menus.Command;
public class AddNumber extends Command<Sequence> {
public AddNumber(Sequence ent) {
super(Label.ADD_NUMBER_TITLE, ent);
addIntegerField("number", Prompt.numberToAdd());
}
protected void execute() { // executed when this option is selected
int number = integerField("number");
int currentSize = _receiver.addNumber(number);
_display.popup(Message.sequenceLength(currentSize, _receiver.distinctNumbers()));
}
}
A realização da funcionalidade deste comando é realizada no método execute() e é feita à custa da funcionalidade do objecto Sequence associado ao comando e da funcionalidade da framework po-uilib. Primeiro, acedemos ao número a adicionar à sequência invocando o método integerField() sobre o formulário por omissão. Este método vai devolver o número inserido relativo ao campo do formulário indicado (note-se que o formulário por omissão já foi preenchido pelo utilizador imediatamente antes da execução do método execute()). Depois invoca-se o método addNumber() sobre a entidade do domínio associada ao comando (e acessível através do atributo _receiver) e apresenta-se o valor devolvido por este método ao autilizador invocando o método popup() sobre o Display assoaciado ao comando.