Lab4 Diogo Cassimiro

aluno: Diogo de Albuquerque Cassimiro
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 05/11/2008 (6)

Introdução

Basicamente o que foi feito neste laboratório foi passar o programa feito no laboratório 2 de java para ruby para assimiliarmos os conceitos dessa linguagem

Desenvolvimento

A parte mais simples do desenvolvimento foi a troca da sintaxe dos elementos envolvidos, contudo devido as diferenças da linguagem em si permitiram algumas mudanças na implementação do programa, tornando-o mais sucinto e mais fácil de escrever, embora simplesmente trocar a sintaxe fosse mais fácil, ao se pensar um pouco facilmente se viam oportunidades de reduzir o código escrito e por outras vezes se tornava necessário alterações mais estruturais do código somente para a implementação do programa.

O fato de ruby possuir tipagem dinâmica já simplificava em muito a digitação do código, pois nos dispensava a declaração de tipos e interfaces e também nos permitia o uso de um poilmorfismo sem limites, isto é, todo objeto pode assumir qualquer forma desde que no final das contas consiga desempenhar o que foi requerido no código.

em ruby não tinhamos a necessidade da separação em arquivos de classes, contudo fizemos uma separação a fim de manter o código organizado, separamos nos seguintes arquivos:

alunos.rb : classes dos alunos de diferentes faculdades
comportamentos.rb: classes dos comportamentos dos alunos
sistema.rb: classes do sistema escolar, professor, relatorio etc.
testes.rb: onde fica a nossa classe de testes
main.rb: onde é implementado o simulador

Nas classes dos alunos o que fizemos basicamente foi traduzir o código original com pequenas simplificações naturais da linguagem como os getters and setters, ficando com o seguinte código:

require 'comportamentos'
 
class Aluno
  @conhecimento
  @nome
  @queue
  @comportamento
  def initialize(nome, queue)
    @comportamento= Esforcado.new(self)
    @nome = nome
    @conhecimento = 0
    @queue = queue
  end
  def faz_e_entrega_relatorio
    @conhecimento= @conhecimento +1
    qualidade = (2*@comportamento.dedicacao+1*@comportamento.inteligencia)/3.0
    originalidade = (1*@comportamento.dedicacao+ 2*@comportamento.inteligencia)/3.0
    rela = Relatorio.new(qualidade,originalidade,self)
    @queue.queue(rela)
  end
  def inteligencia
    @comportamento.inteligencia
  end
  def dedicacao
    @comportamento.dedicacao
  end
  attr_reader :nome 
  attr_reader :conhecimento
  attr_writer :comportamento
end
 
class AlunoITA < Aluno
  def initialize(nome, queue)
    super
  end 
  def comportamento=(comportamento)
    raise("comportamento invalido") if comportamento.class==Burro 
    super
  end
end
 
class AlunoUnicamp < Aluno
  def initialize(nome, queue)
    super
  end  
  def comportamento= (comportamento) 
    raise("comportamento invalido") if comportamento.class==Summa||comportamento.class==SafoPreguicoso
    super
  end
end
 
class AlunoUSP < Aluno
  def initialize(nome, queue)
    super
  end
  def comportamento=(comportamento)
    raise("comportamento invalido") if comportamento.class==Summa||comportamento.class==SafoPreguicoso
    super
  end
end

Para os comportamentos houve uma mudança estrutural, não havia mais a necessidade de uma interface comportamento devido a tipagem dinâmica, primeiramente pensamos em usar módulos para atribuir os comportamentos aos alunos, mas percebemos que com o uso de módulos se quiséssemos mudar o comportamento de um aluno, teríamos que sobrescrever um módulo ao outro, o que seria no mínimo feio, já que não temos como remover o módulo anterior adicionado ao objeto, assim continuamos interpretando um comportamento como uma classe onde o aluno teria um objeto desta classe que definiria seu comportamento. Por um lado não precisar de uma interface simplifica o código tornando-o mais sucinto, contudo isto gera uma gama de erros possíveis em tempo de execução que não podem ser detectados no momento que se escreve o código devido a tipagem dinâmica, desta forma temos um código mais sucinto porém mais suscetível a erros. Ficamos com os comportamentos desse jeito:

class Esforcado 
   @aluno
  def initialize(aluno)
    @aluno = aluno
  end
  def inteligencia
    0.5
  end
  def dedicacao
    1
  end
end
 
class Burro 
  @aluno
  def initialize(aluno)
    @aluno = aluno
  end
  def inteligencia
    0
  end
  def dedicacao
    0
  end
end
 
class Imprevisivel  
   @aluno
  def initialize(aluno)
    @aluno = aluno
  end
  def inteligencia
    rand
  end
  def dedicacao
    rand
  end
end
 
class Pemba   
   @aluno
  def initialize(aluno)
    @aluno = aluno
  end
  def inteligencia
    0.5*rand
  end
  def dedicacao
    0.5*rand
  end
end
 
class Summa   
   @aluno
  def initialize(aluno)
    @aluno = aluno
  end
  def inteligencia
    1
  end
  def dedicacao
    1
  end
end
 
class SafoPreguicoso  
  @aluno
  def initialize(aluno)
    @aluno = aluno
  end
  def inteligencia
    1
  end
  def dedicacao
    0.5*rand
  end
end

Em seguida fizemos a tradução dos elementos do sistema universitário, o professor, o relatório e a queue de relatórios. O professor e o relatorio foram uma mera tradução suscinta do código, já a queue era uma classe que não era em si necessária já que o um array tem as funções que queríamos que a queue desempenhasse, mas por uma questão de desacoplamento, utilizamos a queue, isto é caso quiséssemos fazer o nosso programa por outro método que não por arrays poderíamos sem mudar o resto do código, mas de qualquer forma por causa da tipagem dinâmica não faz sentido falar em generics já que tudo é genérico em ruby, então apenas fizemos uma queue que servirá para qualquer tipo de objeto. Segue o código do arquivo sistema

class Professor
  @queue
  def initialize(queue)
    @queue= queue
  end
 
  def corrigir_relatorios()
    puts "\t\nnotas:"
    while(rela =@queue.dequeue)
      puts "#{rela.aluno.nome} - #{corrigir_relatorio(rela)}"
    end
  end
  private 
  def corrigir_relatorio(rela)
    10*(rela.qualidade + rela.originalidade + rand)/3 
  end
end
 
class Queue
  def initialize
  @queue = Array.new
  end
  def queue(obj)
    @queue.push(obj)
  end
  def dequeue
    @queue.shift
  end
end
 
class Relatorio
  @qualidade
  @originalidade
  @aluno
  def initialize (qualidade, originalidade, aluno)
    @qualidade = qualidade
    @originalidade = originalidade
    @aluno =aluno    
  end  
  attr_reader :qualidade ,:originalidade,:aluno
end

Em seguida para implementação da classe simulador que não nos ofereceu muitos problemas, não houve nem mesmo a necessidade de criação de uma classe, sendo basicamente este arquivo composto por programação estruturada e as partes de orientação a objeto provenientes dos outros arquivos, na verdade a implementação desta parte foi extremamente mais fácil do que em java, a função eval que permite vc rodar o conteúdo de uma string nos permitiu a criação de objetos dinamicamente a partir do nome do mesmo de forma bem mais simples do utilizando a classe reflector em java. Contudo é fácil ver que este tipo de abordagem de programação é sujeita a runtime errors que não podem ser detectados antes de se rodar, e também a atitudes invasivas, como inserção de trechos de código maliciosos. Segue o simulador, que está no arquivo main.rb:
require 'comportamentos'
require 'alunos'
require 'sistema'
 
def cria_aluno (facul, nome, queue)
  facul= "Aluno" + facul
  s = "#{facul}.new(nome,queue)"
  if facul=="AlunoITA"||facul=="AlunoUSP"||facul=="AlunoUnicamp"
    return eval("#{s}") 
  else
    return Aluno.new(nome,queue)
  end
end
 
def cria_comportamento(comp,aluno)
   if comp == "SafoPreguicoso"||comp == "SafoPreguicoso"||comp =="Summa"||comp =="Pemba"||comp =="Burro"||comp =="Esforcado"||comp =="Imprevisivel"      
    return eval("#{comp}.new(aluno)")
   else
return Esforcado.new(aluno) 
   end
end 
 
loop do
  queue = Queue.new
  professor = Professor.new(queue)
  #criando os alunos
  puts("Digite o numero de alunos que deseja criar: ")
  num_alunos = gets().to_i
  break if num_alunos==0
  alunos = Array.new(num_alunos)
  alunos.each_index { |index|      
    puts " #{index+1} - Nome : " 
    nome = gets.chomp
    puts " Comportamento:(Burro/Esforcado/Imprevisivel/Pemba/SafoPreguicoso/Summa) "
    comp = gets.chomp
    puts " Proveniencia:(ITA/USP/Unicamp) "
    facul = gets.chomp
    alunos[index]=cria_aluno(facul,nome,queue)
    comportamento = cria_comportamento(comp, alunos[index])
    begin
    alunos[index].comportamento= comportamento
    rescue  Exception => e
     puts "#{e.to_s}"
    # por default ele permanecerá esforçado
    end
    }
     #avaliando relatorios
   #alunos entregam os relatorios
    alunos.each { |aluno| aluno.faz_e_entrega_relatorio }
    #professor corrige os relas
    professor.corrigir_relatorios
 end

Em seguida fizemos a tradução da classe de testes para ruby, e tivemos alguns problemas para rodar, pois o endereço do executável do ruby não estava na variável path do windows, mas corrigido isso, fizemos os devidos testes e obtivemos sucesso

require 'alunos'
require 'sistema'
require 'comportamentos'
require 'test/unit' 
 
class Testes < Test::Unit::TestCase
 def test_funciona_como_o_lab1
    #criando os objetos
    queue = Queue.new    
    prof = Professor.new(queue)
    aluno1 = Aluno.new("John Smith",queue)
    aluno2 = Aluno.new("Mark Smith",queue)
    aluno3 = Aluno.new("Joseph Smith",queue)
    aluno4 = Aluno.new("Robert Smith",queue)
    begin
      aluno3.comportamento = SafoPreguicoso.new(aluno3)
      aluno4.comportamento = SafoPreguicoso.new(aluno4)
    rescue Exception => e
      puts "Não deveria lançar erro!"
    end
 
    #teste de aluno esforcado
    assert_equal(0.5, aluno1.inteligencia,0.01)
    assert_equal(1, aluno1.dedicacao,0.01)
 
    #teste de alunosafo
    assert_equal( 1.0 ,aluno3.inteligencia,0.01)
    assert( aluno3.dedicacao != aluno3.dedicacao) #deve ser randomica
    assert( aluno3.dedicacao < 0.5)
 
    #alunos comecam a fazer os relas
    aluno1.faz_e_entrega_relatorio
 
    #roubamos o relatorio do primeiro aluno para investiga-lo
    rela1 = queue.dequeue
    assert_equal(aluno1, rela1.aluno)
    assert(rela1.aluno.nome=="John Smith")
    assert(rela1.qualidade <0.9)
    assert(rela1.qualidade >0.8)
    assert(rela1.originalidade <0.7)
    assert(rela1.originalidade >0.6)
 
    #os outros continuam a fazer os relas
 
    aluno2.faz_e_entrega_relatorio
    aluno3.faz_e_entrega_relatorio
    aluno4.faz_e_entrega_relatorio
 
    #deve exibir a correção dos relas dos alunos2,3 e 4
    prof.corrigir_relatorios
  end
 
  def test_os_comportamentos_funcionam_como_especificado
    queue = Queue.new
    aluno1 = Aluno.new("John",queue)
    summa = Summa.new(aluno1)
    pemba = Pemba.new(aluno1)
    imprevisivel = Imprevisivel.new(aluno1)
    safo = SafoPreguicoso.new(aluno1)
    pemba = Pemba.new(aluno1)
    esforcado = Esforcado.new(aluno1)
    burro = Burro.new(aluno1)
 
    #teste de esforcado
    assert_equal(0.5, esforcado.inteligencia,0.01)
    assert_equal(1, esforcado.dedicacao,0.01)
 
    #teste de burro
    assert_equal(0.0, burro.inteligencia,0.01)
    assert_equal(0.0, burro.dedicacao,0.01)
 
    #teste de summa
    assert_equal(1.0, summa.inteligencia,0.01)
    assert_equal(1.0, summa.dedicacao,0.01)
 
    #teste de safo
    assert_equal(1.0, safo.inteligencia,0.01)
    assert(safo.dedicacao != safo.dedicacao) #deve ser randomica
    assert(safo.dedicacao <1.0 ) #deve ser randomica
 
    #teste de imprevisivel
    assert(imprevisivel.inteligencia != imprevisivel.inteligencia) #deve ser randomica
    assert(imprevisivel.inteligencia <1.0 ) 
    assert(imprevisivel.dedicacao != imprevisivel.dedicacao) #deve ser randomica
    assert(imprevisivel.dedicacao <1.0 ) 
 
    #teste de pemba
    assert(pemba.inteligencia != imprevisivel.inteligencia) #deve ser randomica
    assert(pemba.inteligencia <0.5 ) 
    assert(pemba.dedicacao != imprevisivel.dedicacao) #deve ser randomica
    assert(pemba.dedicacao <0.5 ) 
  end
 
  def test_as_subclasses_de_aluno_tem_restricoes
    queue = Queue.new
    aluno_ita = AlunoITA.new("John Smith",queue)
    aluno_usp = AlunoUSP.new("Mark Smith",queue)
    aluno_unicamp = AlunoUnicamp.new("Joseph Smith",queue)
    begin 
     aluno_ita.comportamento = SafoPreguicoso.new(aluno_ita)
     aluno_ita.comportamento = Summa.new(aluno_ita)
     aluno_usp.comportamento = Burro.new(aluno_usp)
     aluno_unicamp.comportamento = Burro.new(aluno_unicamp)
    rescue Exception  => e
     puts "não deveria lançar erro"
    end
    begin 
      aluno_ita.comportamento = Burro.new(aluno_ita)
      puts "deveria lançar erro"
    rescue Exception => e
    end
    begin 
      aluno_unicamp.comportamento= Summa(aluno_unicamp)
      puts "deveria lançar erro"
    rescue Exception => e
    end
  end
end

Não utilizamos a função assert_raise pois por algum motivo ela não reconhecia a exception gerada no comprotamento nos alunos, segue o resultado dos testes:

lab4ces22.jpg

Conclusão

Foi possível a familizarização com vários aspectos da linguagem ruby, desde as diferenças de sintaxe até as diferenças de implementação e as decorrências no código dessas diferenças, bem como pode-se reparar nas diferenças da tipagem do ruby para o java. Notamos que o ruby perde em performance e em robustez mas ganha em rapidez de produção.

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