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:
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.





