aluno: Yves Conselvan
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 20/08/2008 (4)
Introdução
Neste laboratório continuamos o desenvolvimento do sistema de simulação iniciado no primeiro laboratório.
Primeiramente implementamos a classe Simulador para manipular objetos das classes anteriormente criadas. Para a execução dessa tarefa achei necessário efetuar primeiro a trasformação da lista de relatórios para tipos genéricos, ficando mais fácil manipular uma lista de alunos. Para facilitar o acesso à lista de alunos implementei a interface Iterator na classe Queue, deixando apenas o método remove em branco, pois exigiria complexidade adicional (e não se mostrou necessário até a atual implementação).
Após a implementação inicial da classe Simulador seguiu-se a modificação na estrutura da classe Aluno eliminando a herança e inserido a classe Comportamento pra representar características específicas de cada diferente aluno. Para a definição dos comportamentos adicionais fiz algumas alterações na classe Comportamento de forma que fosse possível definir no construtor se a propriedade era pseudo-aleatória, e nesse caso um valor máximo, ou não pseudo-aleatória, e nesse caso o valor da propriedade. Dessa forma foi possível não ficar repetindo métodos muito parecidos para sobreescrever as funções de acesso das propriedades para colocar o código que gerava o valor pseudo-aleatório.
O próximo passo foi a definição de diferentes tipos de instituição para os alunos, criando sub-classes de Aluno para cada uma das instituições, e consequentemente a alteração da classe Simulador para suportar as novas funcionalidades.
Desenvolvimento
O passo inicial do desenvolvimento foi a criação da classe Simulador para fazer a interface entre os objetos e o usuário. Optei por uma solução baseada em menus. A idéia era que na tela inicial fosse possível inserir alunos, mandar que os alunos façam e entreguem os relatórios, ou mandar que o professor efetue a correção dos relatórios. Para atingir este objetivo foi necessária a utilização de uma lista de alunos, de forma que os alunos são inseridos na lista e quando for selecionada a opção para fazer e entregar os relatórios itera-se pela lista de alunos chamado-se o método fazEEntregaRelatorio() em cada um deles. Sendo assim achei importante realizar primeiramente o item 5 do laboratório, de forma que a classe Simulador utilizasse a Queue genérica tanto para manter a lista dos relatórios quanto dos alunos. A antiga classe RelaQueueItem passou a se chamar QueueItem, e a classe Queue absorveu o código que estava na RelaQueue. RelaQueue apenas é sub-classe de Queue com o tipo definido.
Apresento o código das classes QueueItem, Queue e Simulador.
Classe QueueItem
//classe agora declarada com o tipo generico T public class QueueItem<T> { //o item aponta para o tipo genérico T private T item; private QueueItem next; //construtor da classe //recebe a referencia de um item T public QueueItem(T item) { this.item = item; this.next = null; } //retorna o proximo item da fila public QueueItem getNext() { return this.next; } //seta o proximo item da fila public void setNext(QueueItem p) { this.next = p; } //retorna o item T referenciado por este item da lista public T getItem() { return this.item; } }
Classe Queue
import java.util.Iterator; public class Queue<T> implements Iterator { private QueueItem<T> inicio, fim, iterador; //construtor da classe //cria um nó cabeça public Queue() { inicio = new QueueItem<T>(null); fim = inicio; iterador = inicio; } //coloca um item na fila public void queue(T rela) { fim.setNext(new QueueItem(rela)); fim = fim.getNext(); } //retira um item da fila public T dequeue() { T ret = null; if (inicio.getNext() != null) { //este casting gerou um warning de operação insegura ou não checada //sem ele dá erro ret = (T)inicio.getNext().getItem(); if (inicio.getNext() == fim) { fim = inicio; } inicio.setNext(inicio.getNext().getNext()); } return ret; } //retorna se o iterator tem um proximo item public boolean hasNext() { boolean ret = true; if (iterador.getNext() == null) { ret = false; iterador = inicio; } return ret; } //retorna o proximo item do iterador public T next() { T ret = null; //verifica se tem um proximo if (this.hasNext()){ //este casting gerou um warning de operação insegura ou não checada //sem ele dá erro ret = (T)iterador.getNext().getItem(); iterador = iterador.getNext(); } else { iterador = inicio; } return ret; } //remove o item apontado pelo iterador //não implementado public void remove() { } }
Classe Simulador
import java.io.InputStreamReader; import java.io.Console; public class Simulador { //lista de alunos private static Queue<Aluno> alunos = new Queue<Aluno>(); //lista de realtorios, matem o tipo RelaQueue private static RelaQueue relas = new RelaQueue(); //InputStreamReader para private static InputStreamReader cin = new InputStreamReader(System.in); private static Professor professor; public static void main(String args[]) { professor = new Professor(relas); menu(); } //laço do menu principal private static void menu() { int opcao; boolean fim = false; do { System.out.println("\n1 - Inserir alunos\n2 - Entrega de relatorios\n3 - Correcao de relatorios\n4 - Sair"); try { //laço para pegar uma opção válida do { opcao = cin.read(); } while (opcao < 49 || opcao > 52); switch (opcao) { case 49: insereAluno(); break; case 50: entregaRelatorios(); break; case 51: corrigeRelatorios(); break; case 52: fim = true; break; } } catch(Exception e) { System.out.println("Erro ao ler opcao"); System.out.println(e.toString()); } } while (!fim); } //insere um aluno na lista private static void insereAluno() { int opcao; Aluno novo_aluno; String nome; System.out.println("\nInserir aluno"); //tenta ler o nome do aluno //deu problema ao tentar rodar dentro do BlueJ //parece que a tela de saída do BlueJ não funciona bem como um console try { Console c = System.console(); nome = c.readLine("\nNome: "); } catch (Exception e) { System.out.println("Nao foi possivel abrir o console"); System.out.println("Nao sera possivel inserir um aluno"); System.out.println("Tente utilizar o programa a partir de um console do sistema"); return; } //passa o nome lido para a função que seleciona o tipo do aluno novo_aluno = selecionaTipo(nome); if (novo_aluno == null) { //nao criou o aluno return; } //insere o novo aluno na lista alunos.queue(novo_aluno); } //seleciona o tipo do aluno e retorna um objeto do tipo selecionado private static Aluno selecionaTipo(String nome) { Aluno aluno = null; int opcao; System.out.println("Selecione o tipo:\n1 - Esforcado\n2 - SafoPreguicoso"); try { //laço para pegar uma opção válida do { opcao = cin.read(); } while (opcao < 49 || opcao > 50); switch (opcao) { case 49: aluno = new AlunoEsforcado(nome, relas); break; case 50: aluno = new AlunoSafoPreguicoso(nome, relas); break; } } catch(Exception e) { System.out.println("Erro ao ler opcao"); } return aluno; } //itera pelos alunos da lista e manda fazer e entregar o relatório private static void entregaRelatorios() { Aluno aluno; System.out.println("\nEntrega de relatorios\nAlunos na lista de alunos:"); while (alunos.hasNext()) { aluno = alunos.next(); System.out.println(aluno.getNome()); aluno.fazEEntregaRelatorio(); } } //manda o professor corrigir os relatórios private static void corrigeRelatorios() { System.out.println("\nCorrecao de relatorios"); professor.corrigirRelatorios(); } }
Nas modificações da classe Aluno surgiu a nova classe Comportamento. Para tentar generalizar mais os comportamentos criei um construtor que permite selecionar se alguma das propriedades é aleatória e neste caso um valor máximo para a propriedade. Se a propriedade não é aleatória então o valor passado será tratado como o valor da propriedade.
Apresento abaixo o código da classe comportamento e de suas sub-classes.
Classe Comportamento
import java.util.Random; public abstract class Comportamento { private float inteligencia; private float dedicacao; private boolean inteligencia_aleatoria = false; private boolean dedicacao_aleatoria = false; private Aluno aluno; //construtor para inteligencia e dedicacao determinadas public Comportamento(Aluno aluno, float inteligencia, float dedicacao) { this.aluno = aluno; this.inteligencia = inteligencia; this.dedicacao = dedicacao; } //construtor com possibilidade de selecionar uma propriedade como aleatoria public Comportamento(Aluno aluno, boolean inteligencia_aleatoria, float inteligencia, boolean dedicacao_aleatoria, float dedicacao) { this.aluno = aluno; this.inteligencia = inteligencia; this.dedicacao = dedicacao; this.inteligencia_aleatoria = inteligencia_aleatoria; this.dedicacao_aleatoria = dedicacao_aleatoria; } //retorna a inteligencia public float getInteligencia() { if (inteligencia_aleatoria) { return valorAleatorio(this.inteligencia); } else { return this.inteligencia; } } //retorna a dedicação public float getDedicacao() { if (dedicacao_aleatoria) { return valorAleatorio(this.dedicacao); } else { return this.dedicacao; } } //gera um valor aleatorio com um maximo private float valorAleatorio(float maximo) { Random generator = new Random(); float r = generator.nextFloat(); return r * maximo; } }
Classe Imprevisivel
public class Imprevisivel extends Comportamento { public Imprevisivel(Aluno aluno) { super(aluno, true, 1, true, 1); } }
Classe Pemba
public class Pemba extends Comportamento { public Pemba(Aluno aluno) { super(aluno, true, 0.5f, true, 0.5f); } }
Classe Esforcado
public class Esforcado extends Comportamento { public Esforcado(Aluno aluno) { super(aluno, 0.5f, 1); } }
Classe SafoPreguicoso
public class SafoPreguicoso extends Comportamento { public SafoPreguicoso(Aluno aluno) { super(aluno, false, 1, true, 0.5f); } }
Classe Burro
public class Burro extends Comportamento { public Burro(Aluno aluno) { super(aluno, 0, 0); } }
Classe Summa
public class Summa extends Comportamento { public Summa(Aluno aluno) { super(aluno, 1, 1); } }
Após a implementação destas novas classes a classe Aluno deixou de ser abstrata e deixaram de existir as sub-classes AlunoSafoPreguicoso e AlunoEsforçado.
Apresento abaixo as principais alterações na classe Aluno
//classe agora não é mais abstrata public class Aluno { ... //referencia a um comportamento private Comportamento comportamento; ... public Aluno(String nome, RelaQueue queue) { ... //seta como padrão o comportamento esforçado this.comportamento = new Esforcado(this); } //retorna a inteligencia a partir do comportamento public float getInteligencia() { return comportamento.getInteligencia(); } //retorna a dedicação a partir do comportamento public float getDedicacao() { return comportamento.getDedicacao(); } ... //novo método para setar o comportamento public void setComportamento(Comportamento comportamento) throws Exception { this.comportamento = comportamento; } }
Após esta alteração foi simples fazer uma implementação para várias instituições utilizando herança a partir da classe Aluno.
Apresento abaixo o código da três subclasse criadas AlunoITA, AlunoUSP e AlunoUnicamp
Classe AlunoITA
public class AlunoITA extends Aluno { public AlunoITA(String nome, RelaQueue queue) { super(nome, queue); } public void setComportamento(Comportamento comportamento) throws Exception { if(comportamento instanceof Burro) { throw new Exception(); } super.setComportamento(comportamento); } }
Classe AlunoUSP
public class AlunoUSP extends Aluno { public AlunoUSP(String nome, RelaQueue queue) { super(nome, queue); } public void setComportamento(Comportamento comportamento) throws Exception { if(comportamento instanceof Summa || comportamento instanceof SafoPreguicoso) { throw new Exception(); } super.setComportamento(comportamento); } }
Classe AlunoUnicamp
public class AlunoUnicamp extends Aluno { public AlunoUnicamp(String nome, RelaQueue queue) { super(nome, queue); } public void setComportamento(Comportamento comportamento) throws Exception { if(comportamento instanceof Summa || comportamento instanceof SafoPreguicoso) { throw new Exception(); } super.setComportamento(comportamento); } }
Após estas novas implementações foi necessário fazer modificações na classe Simulador de forma que o usuário pudesse fazer a seleção de instituição e comportamento. O método insereAluno fo modificado, o método selecionaTipo saiu e surgiram os novos métodos selecionaInstituicao e selecionaComportamento. Apresento abaixo fragmentos da classe simulador com as alterações.
public class Simulador { ... private static void insereAluno() { int opcao; Aluno novo_aluno; Comportamento comportamento; Console c = System.console(); System.out.println("\nInserir aluno"); String nome = c.readLine("\nNome: "); novo_aluno = selecionaInstituicao(nome); if (novo_aluno == null) { return; } alunos.queue(novo_aluno); comportamento = selecionaComportamento(novo_aluno); if (comportamento != null) { try { novo_aluno.setComportamento(comportamento); } catch(Exception e) { System.out.println("Operacao nao permitida"); } } } private static Aluno selecionaInstituicao(String nome) { Aluno aluno = null; int opcao; System.out.println("Selecione uma instituicao: \n1 - ITA\n2 - USP\n3 - Unicamp"); try { do { opcao = cin.read(); } while (opcao < 49 || opcao > 51); switch (opcao) { case 49: aluno = new AlunoITA(nome, relas); break; case 50: aluno = new AlunoUSP(nome, relas); break; case 51: aluno = new AlunoUnicamp(nome, relas); break; } } catch(Exception e) { System.out.println("Erro ao ler opcao"); } return aluno; } private static Comportamento selecionaComportamento(Aluno aluno) { Comportamento comportamento = null; int opcao; System.out.println("Selecione o comportamento:"); System.out.println("1 - Burro\n2 - Esforcado"); System.out.println("3 - Imprevisivel\n4 - Pemba"); System.out.println("5 - SafoPreguicoso\n6 - Summa"); try { do { opcao = cin.read(); } while (opcao < 49 || opcao > 54); switch (opcao) { case 49: comportamento = new Burro(aluno); break; case 51: comportamento = new Imprevisivel(aluno); break; case 52: comportamento = new Pemba(aluno); break; case 53: comportamento = new SafoPreguicoso(aluno); break; case 54: comportamento = new Summa(aluno); break; } } catch(Exception e) { System.out.println("Erro ao ler opcao"); } return comportamento; } ... }
Apresento abaixo as telas com o diagrama de classes do BlueJ, tela de teste e saída dos testes.
Diagrama de classes
Tela de testes
Saída dos testes
Abaixo apresento a execução a partir de um console do método main da classe Simulador
Conclusão
Este laboratório foi uma continuação do primeiro laboratório e apresentou um nível de complexidade um pouco maior. O código da lista genérica ficou por nossa conta, acho que isso estimula o desenvolvimento de código.
Não consegui rodar o método main da classe Simulador a partir do BlueJ, pois não era possível pegar o console, foi necessário rodar a partir da linha de comando.
Uma questão que ainda apresenta dificuldades de escalabilidade é a necessidade de se utilizar switch's para instanciar cada um dos diferentes tipos de classe.
corrigido. a "solução" para o uso dos switchs são os padrões Factory que vimos em aula.