aluno: Ricardo Itiro Sabota Tominaga
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 02/09/2008 (6)
Introdução
Nesse laboratório foi feito uma espécie de "update" do laboratório anterior. Foi criada uma classe que contém um método "main", possibilitando a realização de IO de dados pelo próprio console. Foram aplicados outros conceitos de OO como delegação e desacoplamento. Além disso, foi implementada uma solução de tratamento de exceções com "throw-try-catch exception". E também, criou-se uma classe para fila de tipo genérico, para, dessa forma, ser herdada por RelaQueue, que apenas especifica o tipo de objeto na fila.
Desenvolvimento
Interface para IO
Para a realização da interface com o usuário foi criada uma classe chamada de Simulador, a qual possui o método "main", responsável por realizar a execução principal do código. Dentro desse método foram instanciados os objetos necessários para a execução de um procedimento completo como no laboratório 1, no entanto sem a necessidade de faze-lo manualmente.
Console c; char flag=0; RelaQueue queue = new RelaQueue(); Professor prof = new Professor(queue); Vector<Aluno> Alunos = new Vector<Aluno>(); String s = new String();
Utilizando-se o objeto do tipo Console pôde-se realizar a aquisição de dados, bem como sua impressão no console.
... c = System.console(); do{ printMenu1(c); s = c.readLine("\nOpcao: "); ... c.printf("\n\nABSURDO! O aluno sera, por definicao, esforcado!"); ...
Um exemplo da utilização da interface IO com o usuario através do console (no caso do próprio DOS, pois não foi possível pelo BlueJ).
Img.1 Exemplo da interface IO com o usuário
Desacoplamento, delegação e coesão
Inicialmente, no lab1, especificou-se apenas comportamentos de Aluno. Quando se desejou adicionar mais uma classificação, como a instituição que um determinado aluno estuda, o número de subclasses de Aluno aumentou, resultando em excesso de classes e repetição de código. Esse código seria, em princípio, muito acoplado, ou seja, praticamente tudo é feito e definido por Aluno. Por esse mesmo motivo, o código não tinha muita coesão, uma vez que o código restringia o seu reuso. Uma solução para desacoplar o código é a delegação. Percebeu-se que era possível separar o comportamento da instituição, ou seja, a criação de uma classe responsável apenas pelo comportamento.
public abstract class Comportamento { Aluno aluno; public Comportamento(Aluno aluno){ this.aluno=aluno; } public abstract float getInteligencia(); public abstract float getDedicacao(); }
Sendo abstrata, pode-se utilizar a herança para definir os comportamentos. Por exemplo:
public class Summa extends Comportamento { private float inteligencia=1; private float dedicacao=1; public Summa(Aluno aluno){ super(aluno); } public float getInteligencia(){ return this.inteligencia; } public float getDedicacao(){ return this.dedicacao; } }
Img.2 Imagem que mostra instanciação manual e o diagrama de classes
Pela imagem 2 é possível perceber quão complexo é o código, no entanto se mostra organizado e bem desacoplado (desconsiderando as linhas de uso). Considerando-se que sem a delegação teríamos 6 (comportamentos) X 3 (instituições) = 18 subclasses (aproximadamente, pois há instituições que não admitem certos comportamentos), reduziu-se a necessidade de subclasses pela metade, bem como a repetição de código.
Os problemas de desacoplamento e coesão poderiam ser resolvidos, também, com a criação de uma classe abstrata Instituição, a qual definiria, por composição, a instituição do aluno, considerando até uma mudança de instituição (da mesma forma como foi feito o comportamento). Outra forma de resolver esses problemas é a utilização de interface, pois, cada comportamento diferente é obrigado a implementar todos os métodos da classe abstrata Comportamento, ou seja, é praticamente o mesmo trabalho.
Tratamento de exceções
A solução para tratamento de erros utilizada foi a definida pelas palavras reservadas throw-try-catch. Ela funciona da seguinte maneira: No método que se deseja aplicar uma exceção utiliza-se a palavra reservada "throw", que "lança" uma exceção no momento da chamada do método. Quando o método é chamado, deve-se utilizar o método "try" que tentará executar o pedido, caso haja uma exceção para a chamada daquele método a diretiva "catch" fará o tratamento da exceção.
Exemplo de código:
public void setComportamento(Comportamento comportamento) throws Exception{ if(comportamento instanceof Burro) throw new Exception(); super.setComportamento(comportamento); }
"Lançamento" da exceção
try { Alunos.lastElement().setComportamento(new Burro(Alunos.lastElement())) } catch (Exception e){ c.printf("\n\nABSURDO! O aluno sera, por definicao, esforcado!"); };
"Tentativa" de chamar o método e tratamento da exceção (caso ocorra)
Fila de tipo genérico
No primeiro lab, foi criada uma classe RelaQueue, que era uma fila do tipo Relatório. Isso é muito restritivo, uma vez que só aceita objetos do tipo Relatório. Desejava-se realizar uma fila que pudesse comportar qualquer tipo de objeto. Para isso foi criada uma classe Queue<T>, onde T especifica o tipo da fila no momento da sua utilização no código. Para a construção da classe Queue genérica foi utilizado um objeto do tipo Vector, o qual já aceita tipos genéricos.
Código de Queue<T>:
public class Queue<T> { private Vector<T> Q = new Vector<T>(); private int inicio=0; private int fim=0; public void queue(T t){ Q.add(fim,t); fim=(fim+1)%10; } public T dequeue(){ T aux; if(inicio!=fim){ aux=Q.elementAt(inicio); inicio=(inicio+1)%10; return aux; } else return null; } }
Classe teste
Aqui está a "prova" do funcionamento do código:
Imagem da passagem no teste
Conclusão
Os conceitos de OO servem para o ser humano escrever menos código, sendo esse mais reutilizável e legível. Essa foi a maior conclusão com relação a esse lab. No entanto, na opinião do autor, a solução para a construção de uma fila de genéricos foi um tanto "acochambrada", pois foi utilizado o tipo Vector que já é uma espécie de fila, o que foi feito simplesmente limitou o tamanho da fila e a tornou cíclica. O tratamento de exceções permite a criação de um código robusto, uma vez que pode-se adicionar exceções e tratamento sem a utilização, por exemplo, de flags, ou de valores de retorno.






corrigido. Ótimo rela. O Vector não é uma fila, pois não tem um método para você inserir ou retirar um elemento no começo de um Vector. Vector é basicamente um Array com num de elementos ilimitado e alguns poucos métodos. Esta implementação é "acadêmica" e ilustrativa, o objetivo é treinar OO e encapsulamento, não algorítmos ou estruturas de dados.
Mas você não estava restrito a utilizar nem um Vector nem um Array para implementar a fila, você o fez porque quiz (e porque fica um pouco mais fácil). A gente só "sugeriu" para facilitar a vida do pessoal. Veja o lab do Yves, que usou uma lista ligada.