aluno: Adriano Brasileiro Silva
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 07/09/2008 (6)
Introdução
Nesta prática desenvolvemos o projeto anterior para permitir melhorias a ele:
- Foi criada uma classe que serve de interface com o usuário;
- A estrutura foi alterada para permitir a inclusão de novos tipos de alunos;
- Foi feita uma introdução aos conceitos de exceção;
- Foi criada uma estrutura de filas que pode ser utilizada para qualquer tipo de objeto.
Desenvolvimento
Classe Simulador
Esta classe foi feita de forma a dar uma decisão ao usuário e incluir os alunos até que o usuário decida que não, a partir do que o programa instrui os alunos a fazer os relatórios e o professor (já criado de antemão) a corrigí-los, exibindo no terminal as notas.
Vale notar alguns pontos:
- O sistema de leitura de strings da linguagem é um pouco complicada: para ler o nome dos alunos não há problemas, mas para realizar uma seleção (ler um inteiro) foi necessário fazer o programa ler uma string e convertê-la para inteiro, de modo que, caso o usuário digite um caractere neste ponto, ocorre uma exceção e o programa encerra;
- Foi criada uma array para armazenar os alunos, do tamanho da fila de relatórios, para que se pudesse percorrer esta array instruindo os alunos a realizarem os relatórios;
- O método tenta atribuir os comportamentos aos alunos e, caso não consiga, uma exceção é lançada e o aluno não é adicionado à array (o valor da variável quantidade não é incrementado).
O código da classe é o seguinte:
import java.io.*; public class Simulador { private static BufferedReader stdin = new BufferedReader( new InputStreamReader( System.in ) ); private String aluno; private int selecao; private int quantidade = 1; private Aluno[] alunos = new Aluno[20]; public void main() throws IOException { RelaQueue queue = new RelaQueue(); Professor prof = new Professor(queue); System.out.println("Simulador de correção de relatórios"); do{ System.out.println("\nInserir novo aluno?: (1 - Sim, 2 - Não)"); selecao = Integer.parseInt( stdin.readLine() ); if(selecao == 1){ System.out.println("\nEscreva o nome do aluno:"); aluno = stdin.readLine(); System.out.println("\nIndique a instituição:\n\n1 - ITA\n2 - USP\n3 - Unicamp"); selecao = Integer.parseInt( stdin.readLine() ); switch(selecao){ case 2: alunos[quantidade] = new AlunoUSP(aluno, queue); break; case 3: alunos[quantidade] = new AlunoUnicamp(aluno, queue); break; default: alunos[quantidade] = new AlunoITA(aluno, queue); break; } System.out.println("\nIndique o tipo de aluno:\n\n1 - Summa\n2 - Esforçado\n3 - Safo Preguiçoso\n4 - Imprevisível\n5 - Pemba\n6 - Burro"); selecao = Integer.parseInt( stdin.readLine() ); switch(selecao){ case 1: try{ alunos[quantidade].setComportamento( new Summa(alunos[quantidade]) ); }catch (Exception e) {System.out.println( "Você tentou fazer algo impossível!"); break;} quantidade++; break; case 3: try{ alunos[quantidade].setComportamento( new SafoPreguicoso(alunos[quantidade]) ); }catch (Exception e) {System.out.println( "Você tentou fazer algo impossível!"); break;} quantidade++; break; case 4: try{ alunos[quantidade].setComportamento( new Imprevisivel(alunos[quantidade]) ); }catch (Exception e) {System.out.println( "Você tentou fazer algo impossível!"); break;} quantidade++; break; case 5: try{ alunos[quantidade].setComportamento( new Pemba(alunos[quantidade]) ); }catch (Exception e) {System.out.println( "Você tentou fazer algo impossível!"); break;} quantidade++; break; case 6: try{ alunos[quantidade].setComportamento( new Burro(alunos[quantidade]) ); }catch (Exception e) {System.out.println( "Você tentou fazer algo impossível!"); break;} quantidade++; break; default: try{ alunos[quantidade].setComportamento( new Esforcado(alunos[quantidade]) ); }catch (Exception e) {System.out.println( "Você tentou fazer algo impossível!"); break;} quantidade++; break; } selecao = 1; } }while(selecao != 2); for(int i = 1; i < quantidade; i++) alunos[i].fazEEntregaRelatorio(); System.out.println("\nAs notas são:"); prof.corrigirRelatorios(); } }
Classe Aluno
O código da classe Aluno continua o mesmo, exceto pelos métodos getInteligencia() e getDedicacao():
public double getInteligencia() { return comportamento.getInteligencia(); } public double getDedicacao() { return comportamento.getDedicacao(); }
que retornam os valores de inteligência e dedicação que a classe comportamento retornar:
public abstract class Comportamento { private Aluno aluno; public Comportamento(Aluno x){ aluno = x; } public abstract double getInteligencia(); public abstract double getDedicacao(); }
que, por sua vez, são classes abstratas e são substituídos pelas classes herdeiras.
Essas classes têm todas estrutura parecida, que segue a seguinte conformação:
import java.util.*; public class SafoPreguicoso extends Comportamento { public SafoPreguicoso(Aluno x){ super(x); } public double getInteligencia() { return 1; //Métodos que retornam valores definidos têm essa forma } public double getDedicacao() { Random generator = new Random(); return generator.nextDouble() * 0.5; //Métodos que retornam valores aleatórios têm essa forma } }
Então, ao se criar um aluno, deve-se definir seu comportamento utilizando:
aluno.setComportamento( new Comportamento(aluno) );
A única maneira de se alterar o comportamento do aluno é realmente criando-se a variável comportamento. Então, uma outra maneira de se solucionar este problema é fazer este comportamento ser uma seleção (pode ser um inteiro) e, dependendo se seu valor, as funções getInteligencia() e getDedicação retornariam valores diferentes. Assim, caso se mude o valor do comportamento, tais métodos retornariam valores diferentes. No entanto isso deixaria o código pouco enxuto.
Alunos de instituições diferentes
Para este caso, simplesmente explodi a classe aluno, gerando as classes AlunoITA, AlunoUSP e AlunoUnicamp. O único objetivo dessas classes foi o de verificar se o seu aluno poderia ou não ter o comportamento especificado.
As classes ficarm com a seguinte forma:
public class AlunoUSP extends Aluno { public AlunoUSP(String x, RelaQueue y) { super(x, y); } public void setComportamento(Comportamento c) throws Exception { if(c instanceof Summa || c instanceof SafoPreguicoso) throw new Exception(); super.setComportamento(c); } }
A classe AlunoUnicamp tem a mesma configuração desta classe. Ja a condição na classe AlunoITA é:
if(c instanceof Burro) throw new Exception();
Classe Queue
Esta classe foi feita utilizando a classe Vector do java, como sugerido. No entanto, ao se tentar definir um valor em uma vector utilizando o método "set", caso este valor não tenha sido criado ainda, o programa lança uma exceção. Assim, foi necessário utilizar o método "add" para criar estes objetos no vector. O método "get" funciona normalmente.
Assim, a classe queue é a seguinte:
import java.util.Vector; public class Queue<T> { private Vector<T> array = new Vector<T>(); private int inicio = 0; private int fim = 0; private T obj; public void queue(T obj){ array.add(fim, obj); fim = (fim + 1)%10; } public T dequeue(){ if(inicio != fim) { obj = array.get(inicio); inicio = (inicio + 1) % 10; return obj; } else return null; } }
A antiga classe RelaQueue simplesmente herda dessa classe, tendo o corpo do código vazio, a fim de não precisarmos alterar todo o código da aplicação para se adaptar ao novo Queue.
Resultados
O diagrama da aplicação é o seguinte:
Vamos testar a classe Simulador. Primeiro criamos uma instância dela e executamos o método main():
A seguinte janela de terminal se abre:
Vamos inserir um hipotético aluno Adriano Brasileiro, Aluno do ITA, Burro:
Bom, se no ITA não há alunos burros, vamos tentar um pemba (desta vez vamos chamá-lo de Adriano Brasileiro Silva, para diferenciá-lo do anterior):
Desta vez não ocorreram problemas. Vamos agora inserir um novo aluno Mark Smith, Aluno da USP, Esforçado:
Isto deve ser o bastante. Não queremos mais inserir alunos:
Temos então as notas dos alunos. Note que o aluno Adriano Brasileiro não apareceu pois era um aluno inválido.
Vamos executar a classe de testes:
Vemos que não ocorreram erros. O terminal também se abre revelando suas notas:
Conclusão
Houveram alguns problemas para a leitura de string, já que a linguagem java não tem uma função simples como o scanf() de C, tendo que realizar várias operações para tanto. Aparentemente java também não suporta uma array de genéricos, o que me fez utilizar a classe Vector, como foi descrito. Acredito que strategy realmente seja uma boa implementação que pode ser utilizado para simplificar bastante o código, mas ainda não sei como isso se reflete na velocidade da aplicação (se é mais rápido realizar as operações em uma única classe ou se uma "chamada de classe" leva mais tempo). Talvez fosse bom explorarmos esse assunto.
Você não precisava ter usado o parseInt para transformar a entrada do Readline em inteiro, poderia tê-la tratado como string mesmo, e fazer as comparações usando if…else, resolveria o problema das excessões. No mais tá tudo ok, só faltou a classe simulador antes das alterações.
Quanto a sua indagação a respeito da performance (entre a opção de se fazer a delegação para outro objeto ou implementar o método no próprio objeto), há sim uma pequena "penalidade", mas é um fator linear e desprezível neste caso.
É melhor ter um código legível e fácil de alterar do que um código que tenha uma performance máxima, principalmente quando a diferença de performance é um fator linear. O custo computacional hoje é desprezível perto do custo de desenvolvimento do software (o custo humano), na maioria das aplicações.