Lab2 Diogo Cassimiro

aluno: Diogo Cassimiro
ano/sem: 2008/09/07
data do laboratório (num. da semana) : 06/08/2008 (2)

Introdução

Foi feita uma continuação do laboratório anterior, neste momento procuramos definir mais comportamentos para os alunos e definir possíveis origens para o aluno. Para evitar então uma explosão no número de subclasses, que haveria pois teríamos que criar uma subclasse para cada combinação linear de comportamento e origem do aluno.
Também para não ficarmos restrito somente a uma estrutura testável mas sem real implementação, criou-se um simulador para realmente utilizar o código gerado. em seguida este simulador foi adaptado a nova estrutura de alunos, e por fim generalizamos nossa queue para que ela pudesse armazenar qualquer tipo de objeto em seu interior, não só relatórios, através do uso de generics.
Também para termos algum controle sobre as atribuições de comportamentos a alunos de certas instituições utilizamos lançamento de exceções e tratamento das mesmas quando nos era conveniente.

Desenvolvimento

Primeiramente então criamos o simulador aplicado ainda à estrutura antiga de classes, a criação do simulador foi basicamente a única alteração que tivemos que fazer por enquanto, então segue o código:

import java.util.Scanner;
 
/**
 *
 * @author Diogo
 */
public class Simulador 
{
    public static void main(String[] args)
    {
        Scanner sc =new Scanner(System.in);
        String nome, tipo; 
        RelaQueue queue = new RelaQueue();
        Professor professor = new Professor(queue);
 
        System.out.println(" Digite o numero de alunos que deseja criar: ");
        int numAlunos = sc.nextInt();
        Aluno [] aluno = new Aluno[numAlunos];
        //cria os alunos com os respectivos nomes e comportamento
        for(int i=0;i<numAlunos ;i++)
        {
           System.out.println("aluno "+(i+1)+" :");
           System.out.print("Nome: ");
           nome=sc.next();
           System.out.print("Tipo:(esforcado/safopreguicoso): ");
           tipo=sc.next();
           if(tipo.equals("safopreguicoso"))
           {
               aluno[i]= new AlunoSafoPreguicoso(nome, queue);
           }
           else if(tipo.equals("esforcado"))
           {
               aluno[i]= new AlunoEsforcado(nome, queue);
           }
           else
           {
               System.out.println("tipo especificado nao encontrado, tente novamente");
               i--;           
           }      
        }
         System.out.println("\n\n correcao de relas: \n\n");
        //chicote nos alunos para eles fazerem o relatório
       for(Aluno x:aluno)
       {
       x.fazEEntregaRelatorio();
       }
       //agora é a vez do professor trabalhar
       professor.corrigirRelatorios();
    }
}

A sugestão do console não foi utilizada pois por algum motivo meu programa não tinha acesso ao console, não sei se o sistema operacional bloqueava ou se o compilador impedia, mas de qualquer forma me resumi a usar a classe Scanner para fins de input.

Em seguida então reestruturamos a classe aluno, criando a classe comportamento, e criamos novas subclasses AlunoITA, AlunoUSP, AlunoUnicamp. A classe comportamento era uma classe abstrata, isto tinha a finalidade de definir os métodos getInteligencia() e getDedicacao() de acordo com o comportamento do aluno, e permitia que um aluno mudasse de comportamento, sem ter que morrer e ser ressuscitado, ou melhor, refeito, contudo o fato do da classe comportamento receber o próprio aluno em seu construtor impedia que esta estivesse no construtor do aluno, pois ela não poderia receber este como argumento para cría-lo um momento antes de sua criação. Por isso obrigatoriamente teria que ser utilizado um setComportamento caso se desejasse mudar o comportamento do aluno do padrão, e pelo padrão do bom selvagem todos os alunos nascem esforçados. Para impedir que atrocidades( como um aluno do ITA ser burro) acontecessem em nosso simulador foi implementada nos Alunos das diferentes instituições geração de exceções caso acontecessem determinados comportamentos. Assim segue o código da classe Aluno, um aluno de exemplo AlunoITA, Comportamento e um comportamento de exemplo,Burro:

Aluno:

/**
 *
 * @author Diogo
 */
public class Aluno {
 
public Comportamento comportamento;   
protected int conhecimento;
protected String nome;
protected RelaQueue queue;
 
    public Aluno(String nome, RelaQueue queue)
    {
     this.comportamento=new Esforcado(this);
     this.nome=nome;
     this.queue=queue;
     this.conhecimento=0;
    }
 
    public void fazEEntregaRelatorio()
    {
     conhecimento++;
     float qualidade = (2 * comportamento.getDedicacao() + 1 * comportamento.getInteligencia() ) / 3 ;
     float originalidade = (1 * comportamento.getDedicacao() + 2 * comportamento.getInteligencia() ) / 3 ;
     Relatorio rela = new Relatorio(qualidade, originalidade, this);
     queue.queue(rela);     
    }
    public float getConhecimento()
    {
        return conhecimento;
    }
    public float getInteligencia()
    {
        return comportamento.getInteligencia();
    }
    public float getDedicacao()
    {
        return comportamento.getDedicacao();   
    }
    public String getNome()
    {
       return this.nome;       
    }
    public void setComportamento(Comportamento comportamento) throws Exception 
    {
           this.comportamento=comportamento;       
    }
}

AlunoITA
/**
 *
 * @author Diogo
 */
public class AlunoITA extends Aluno
{
 
    public AlunoITA(String nome, RelaQueue queue) 
    {
        super(nome, queue);
    }
 
    @Override
    public void setComportamento(Comportamento comportamento) throws Exception 
    {
        if(comportamento instanceof Burro) throw new Exception(); 
        super.setComportamento(comportamento); 
    }
 
}

Comportamento

public abstract class  Comportamento {
 
    protected Aluno aluno;
 
    public Comportamento(Aluno aluno) {
        this.aluno = aluno;
    }
 
    public abstract float  getInteligencia();
    public abstract float  getDedicacao();
}

Burro:

public class Burro extends Comportamento
{
 
    public Burro(Aluno aluno)
    {
        super(aluno);    
    }
 
    public float getInteligencia() {
        return 0f;
    }
 
    public float getDedicacao() {
        return 0f;
    }
}

Poderíamos ter pensado em outras soluções para este tipo de problema, poder-se-ia ter ao invés ter tornado o comportamento abstrato, poederíamos ter criado também instituição abstrata para dizer a origem do aluno, assim poderíamos também mudar a instituição do aluno sem recriá-lo. Poderíamos implementar ainda de outra forma, poderíamos definir inteligência e dedicação como uma variável do próprio aluno (o que aliás faz mais sentido do que o atual) e getInteligencia() e getDedicacao() sendo métodos do próprio aluno que chamariam dentro deles a variável comportamento que através de uma modificador via constante ou função daria o resultado. Essa implementação seria melhor pois não precisaríamos mais passar o aluno como parâmetro do comportamento, e assim poderíamos fazer um contrutor onde o comportamento já estivesse incluído, há outras opções também mas esta me parece bem razoável.

Bem continuando, em seguida mudamos o simulador de forma a este se adequar a estrutura de classes utilizada, como pessoalmente não queria que o usuário digitasse um número para escolher a instituição nem o comportamento, e nem reconhecer os strings inseridos pelo usuário numa lista pré-definida para a criação das classes, criei duas funções( apesar de que talvez poderia ter feito uma classe estática, com generics ao invés de duas funções, contudo isso consumiria mais algum tempo e não era o foco do laboratório) para criar objetos a partir de uma string, para tal foi preciso aprender a usar a biblioteca java.lang.reflect e usá-la apropriadamente, o controle de erros poderia ter sido melhor utilizado para filtrar a criação de classes que não fossem comportamentos ou alunos, provavelmente gerando um erro depois por conta do typecasting utilizado em ambos os casos. Por enquanto o código permite este tipo de coisa, mas é possível impedir gerando uma exceção, por exemplo no caso dos alunos, se o objeto gerado não for aluno ou uma extensão deste( ou simplesmente se não for uma extensão de aluno, e como default a saída será um aluno), ou talvez tratando o erro gerado após o typecasting. De qualquer forma segue o simulador, funcionando( mas com algumas brechas):

import java.util.Scanner;
import java.lang.reflect.*;
 
/**
 *
 * @author Diogo
 */
public class Simulador 
{
    public static void main(String[] args)
    {
        Scanner sc =new Scanner(System.in);
        String nome, comportamento,facul; 
        RelaQueue queue = new RelaQueue();
        Professor professor = new Professor(queue);
        System.out.println(" Digite o numero de alunos que deseja criar: ");
        int numAlunos = sc.nextInt();
 
        Aluno [] aluno = new Aluno[numAlunos];
        //cria os alunos com os respectivos nomes,comportamentos, proveniencias
        for(int i=0;i<numAlunos ;i++)
        {
            System.out.println("aluno "+(i+1)+" :");
            System.out.print("Nome: ");
            nome=sc.next();
            System.out.print("Comportamento:(Burro/Esforcado/Imprevisivel/Pemba/SafoPreguicoso/Summa) ");
            comportamento=sc.next();
            System.out.print("Proveniencia: (ITA/USP/UniCamp) ");   
            facul = sc.next();
 
            aluno[i]=(Aluno) criaAluno("Aluno"+facul,queue,nome);
 
            Comportamento a = (Comportamento)criaComp(comportamento,aluno[i]);
            try
            {
                aluno[i].setComportamento(a);
            }
            catch(Exception e)
            {    
                System.out.println("Comportamento inválido para os outros dados do candidato, insira novamente todos os dados");
                i--;
            }
        }
 
       System.out.println("\n\n notas dos relas : \n\n");
       //chicote nos alunos para eles fazerem o relatório
       for(Aluno x:aluno)
       {
       x.fazEEntregaRelatorio();
       }
       //agora é a vez do professor trabalhar
       professor.corrigirRelatorios();
    }
    //criando uma classe aluno qualquer que esta seja
    public static Object criaAluno (String nomeObj,RelaQueue queue,String nome)
    {
        Class [] classParm = new Class[2];
        classParm[0]=String.class;
        classParm[1]=RelaQueue.class;
        Object [] objectParm = new Object[2];
        objectParm[0]=nome;
        objectParm[1]=queue;
        try 
        {
          Class cl = Class.forName(nomeObj);
          Constructor co = cl.getConstructor(classParm);
          return co.newInstance(objectParm);  
        }
        catch (Exception e) 
        {
          //se houver um erro vamos admitir que foi digitada uma faculdade generica
          // portanto teremos um aluno generico
            return new Aluno(nome,queue);
        }
        catch (NoClassDefFoundError e)
        {
            // n sei pq mas o programa n estava considerando isto uma exceçao
            return new Aluno(nome,queue);    
        }     
    }
    //criando uma classe comportamento qualquer q seja este
    public static Object criaComp(String nomeObj,Aluno aluno)
    {
        Class [] classParm = new Class[1];
        classParm[0]=Aluno.class;
        Object [] objectParm = new Object[1];
        objectParm[0]=aluno;
        try 
        {
          Class  cl = Class.forName(nomeObj);
          Constructor co = cl.getConstructor(classParm);
          return co.newInstance(objectParm);  
        }
        catch (Exception e) 
        {
          //se houver um erro vamos admitir que foi digitado um comportamento nao reconhecido
          // e vamos dar como default o comportamento esforcado para estes casos
            return new Esforcado(aluno);
        } 
        catch (NoClassDefFoundError e)
        {
            // n sei pq mas o programa n estava considerando isto uma exceçao
            return new Esforcado(aluno);    
        }             
    }
}

Por último criamos criamos uma classe Queue que pudesse formar uma lista qualquer de objetos utilizando generics, esta implementação em nada mecheu com o simulador pois a interação com o Relaqueue continuava a mesma, apesar de que agora Relaqueue era apenas uma subclasse de Queue aplicado a Relatorio, ou seja era uma Queue aplicada ao tipo Relatorio, pois não possuia nenhum código a mais dentro, o código do Queue em seguida:

import java.util.Vector;
/**
 *
 * @author Diogo
 */
public class Queue<T>
{
 
  private Vector<T> array;  
  int inicio; 
  int  fim;
  public Queue()
  {
    inicio=0;
    fim=0;
    array=new Vector<T>();
  }
  public void queue(T obj)
  {
       if(array.size()>fim)
            array.set(fim,obj);
       else
           array.add(fim, obj);
       fim=(fim+1)%10;
  }
  public T dequeue()
  {
      if(inicio!=fim)
      {  
        T valor= array.get(inicio);
        inicio=(inicio+1)%10;
        return valor;
      }
      else 
      {
        return null;  
      }           
  }
}

Como pode-se ver utilizamos a classe Vector de forma a caso não exista elemento naquele índice ele adiciona ao vetor, do contrário, ele simplesmente troca o elemento existente.

Diagrama UML final do projeto:

lab2ces22UML.jpg

Testamos então o programa com os testes que estavam no laboratório. Inicialmente não havia implementado o método getDedicacao e getInteligenca como parte de Aluno só como comportamento, mas depois que vi os testes de resultado eu o fiz, assim tudo funcionou corretamente.
Resultados de testes:

lab2ces22testresults.jpg

Conclusão

Além do experimento foi muito bem sucedido, blablabla, posso dizer que este experimento foi útil para algumas coisas: aprender a utilizar tipos genéricos, visualizar soluções de problemas de estruturação através lógica de classes e objetos, ou seja, aprender a pensar em objetos como soluções de problemas de implementação, aprender a usar tratamento de exceções para coordenar fluxo do programa e impedir erros, aprender a criar obejtos dinamicamente a partir de string para seu nome e argumentos(embora esta parte tenha sido mais por minha conta, ela foi bem importante, no entanto fez este lab ficar bem mais complicado, recomendo para labs posteriores)

Sugestões e Críticas

Recomendo muito fortemente que se explicite que momentos do código vai se querer no relatório logo no começo das instruções e não no final quando todas as alterações já estão feitas. E não se é o caso, por mim está indiferente, por enquanto, mas é bom variar o tema do programa, pois apesar de bem contextualizado ele pode acabar ficando repetitivo(não tenho certeza mas é bom pensar nisso). Os testes manuais são feitos durante a implementação do programa e depois desta, quando se vai escrever o relatório é pedido que se apresentem os testes mas normalmente estes já foram perdidos, então teoricamente teria que se fazer novos testes em algo que já foi testado, além do que é feito o teste pelos Junits no final, o que torna razoavelmente redundante percorrer tudo isso de novo, e bastante cansativo copiar e colar pedaços de testes manuais neste relatório.

Add a New Comment
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License