aluno: Luty Rodrigues Ribeiro
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 03/11/2008 (6)
Introdução
Neste laboratório, começamos a lidar com a linguagem Ruby, que também uma linguagem orientada a objetos (assim como Java), mas é uma linguagem do tipo dinâmica. Este laboratório visa, principalmente, permitir o contato inicial com Ruby e mostrar diferenças entre Ruby e Java, já que teremos que fazer uma mesma tarefa (no caso, a tarefa do lab2) nas duas linguagens.
Desenvolvimento
Começamos portando as classes do lab2, com algumas diferenças. Para os tipos de comportamento (SafoPreguicoso, Esforcado, Pemba,…), não precisaremos da classe Comportamento, já que não precisamos declarar os tipos das variáveis. Poderíamos implementar a classe comportamento como superclasse dos vários tipos de comportamento, a fim de aproveitar, pelo menos, a herança do construtor, mas não faremos isso para esta situação. Algo semelhante ocorre para as classes AlunoITA, AlunoUSP e AlunoUnicamp. Neste caso, implementamos a classe Aluno como superclasse das três citadas, aproveitamos os métodos e variáveis da classe Aluno, e apenas sobreescrevemos, em cada subclasse, o método "setComportamento", que foi alterado para comportamento=() (que é a forma de escrever um setter em Ruby).
Abaixo, segue os códigos das classes, no arquivo minhas_classes.rb:
class Aluno attr_writer :comportamento attr_reader :nome, :conhecimento def initialize(str, queue) @nome, @queue, @conhecimento, @comportamento = str, queue, 0, Esforcado.new(self) end def faz_e_entrega_relatorio qualidade = (2*@comportamento.dedicacao + @comportamento.inteligencia)/3 originalidade = (@comportamento.dedicacao() + 2*@comportamento.inteligencia())/3 relatorio = Relatorio.new(qualidade, originalidade, self) @queue.queue(relatorio) @conhecimento += 1 end def inteligencia @comportamento.inteligencia end def dedicacao @comportamento.dedicacao end end class AlunoITA < Aluno def comportamento=(comport) raise(Exception.new) if comport.class==Burro @comportamento=comport end end class AlunoUSP < Aluno def comportamento=(comport) raise(Exception.new) if (comport.class==Summa || comport.class==SafoPreguicoso) @comportamento=comport end end class AlunoUnicamp < Aluno def comportamento=(comport) raise(Exception.new) if (comport.class==Summa || comport.class==SafoPreguicoso) @comportamento=(comport) end end class Burro def initialize(aluno) @aluno, @inteligencia, @dedicacao = aluno, 0.0, 0.0 end attr_reader :inteligencia, :dedicacao end class Imprevisivel def initialize(aluno) @aluno = aluno end def inteligencia rand end def dedicacao rand end end class Pemba def initialize(aluno) @aluno = aluno end def inteligencia 0.5*rand end def dedicacao 0.5*rand end end class Esforcado def initialize(aluno) @aluno, @inteligencia, @dedicacao = aluno, 0.5, 1.0 end attr_reader :inteligencia, :dedicacao end class Summa def initialize(aluno) @aluno, @inteligencia, @dedicacao = aluno, 1.0, 1.0 end attr_reader :inteligencia, :dedicacao end class SafoPreguicoso def initialize(aluno) @aluno, @inteligencia = aluno, 1.0 end attr_reader :inteligencia def dedicacao 0.5*rand end end class RelaQueue def initialize @queue = Queue.new end def queue(relatorio) @queue << relatorio end def dequeue @queue.pop end def size @queue.length end end class Professor def initialize(queue) @queue = queue end def corrigir_relatorios n = @queue.size while (n>0) aux = @queue.dequeue puts ("#{aux.aluno.nome}"+": "+"#{corrige_relatorio(aux)}\n") n = @queue.size end end private def corrige_relatorio(rela) nota = 10*(rela.qualidade + rela.originalidade + rand)/3 end end class Relatorio def initialize(qualidade, originalidade, aluno) @qualidade, @originalidade, @aluno = qualidade, originalidade, aluno end attr_reader :qualidade, :originalidade, :aluno end
Observando cada classe, podemos notar uma redução significativa de linhas de código, propiciada pelos atalhos fornecidos pela linguagem (como atribuição em paralelo, attr_acessor), pela tipagem dinâmica (evitando uso de interfaces, classes abstratas como intermediários), etc.
Em seguida, temos o arquivo testes.rb, contendo a classe de teste dada como exemplo e uma outra classe de teste contendo os testes correspondentes do lab2, devidamente reescritos em Ruby:
require 'minhas_classes' #carregue o código da sua aplicação aqui require 'test/unit' #este é só um exemplo, não é toda a classe de testes! class TestInteressante < Test::Unit::TestCase def test_meu_teste queue = RelaQueue.new aluno = AlunoITA.new("John Smith", queue) aluno.comportamento = SafoPreguicoso.new(aluno) assert_equal(1.0, aluno.inteligencia(), 0.01) assert(aluno.dedicacao() < 0.5) assert_raise(Exception) { aluno.comportamento = Burro.new(aluno) } # passa um bloco de código #que é executado e deve gerar exceção end end class Teste < Test::Unit::TestCase def test_funciona_como_o_lab1 queue = RelaQueue.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 fail "Não deveria lançar erro!" end #teste de aluno_esforçado assert_equal(0.5, aluno1.inteligencia , 0.01) assert_equal(1, aluno1.dedicacao, 0.01) #teste de aluno_safo assert_equal(1.0, aluno3.inteligencia, 0.01) assert(aluno3.dedicacao != aluno3.dedicacao) assert(aluno3.dedicacao < 0.5) #alunos começam a fazer os relas aluno1.faz_e_entrega_relatorio #roubamos o relatório do primeiro aluno para investigá-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 alunos aluno2, aluno3 e aluno4, nesta ordem #pois "roubamos" o relatório do aluno1 prof.corrigir_relatorios end def test_os_comportamentos_funcionam_como_especificado queue = RelaQueue.new aluno1 = Aluno.new("John", queue) summa = Summa.new(aluno1) pemba = Pemba.new(aluno1) imprevisivel = Imprevisivel.new(aluno1) safo = SafoPreguicoso.new(aluno1) esforcado = Esforcado.new(aluno1) burro = Burro.new(aluno1) #teste de esforçado 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) assert(safo.dedicacao() < 0.5) #teste de imprevisivel assert(imprevisivel.inteligencia != imprevisivel.inteligencia) assert(imprevisivel.inteligencia < 1.0) assert(imprevisivel.dedicacao != imprevisivel.dedicacao); assert(imprevisivel.dedicacao() < 1.0) #teste de pemba assert(pemba.inteligencia != pemba.inteligencia) assert(pemba.inteligencia < 0.5) assert(pemba.dedicacao != pemba.dedicacao) assert(pemba.dedicacao < 0.5) end def test_as_subclasses_de_aluno_tem_restricoes queue = RelaQueue.new alunoITA = AlunoITA.new("John Smith", queue) alunoUSP = AlunoUSP.new("Mark Smith", queue) alunoUnicamp = AlunoUnicamp.new("Joseph Smith", queue) begin alunoITA.comportamento = SafoPreguicoso.new(alunoITA) alunoITA.comportamento = Summa.new(alunoITA) alunoUSP.comportamento = Burro.new(alunoUSP) alunoUnicamp.comportamento = Burro.new(alunoUnicamp) rescue Exception => e fail "Não deveria lançar erro!" end begin alunoITA.comportamento = Burro.new(alunoITA) fail "Deveria lançar erro!" rescue Exception => e end begin alunoUSP.comportamento = SafoPreguicoso.new(alunoITA) fail "Deveria lançar erro!" rescue Exception => e end begin alunoUnicamp.comportamento = Summa.new(alunoITA) fail "Deveria lançar erro!" rescue Exception => e end end end #Teste de polimorfismo não será necessário para este caso
Abaixo, apresentamos o resultado da execução desta classe de testes pela linha de comando:
Após aprovado nos testes, vamos executar o programa, através do arquivo simulador.rb, partindo novamente da linha de comando:
Se comparamos o resultado obtido acima com o obtido no lab2, a menos da formatação e dos valores numéricos (que dependem de valores gerados aleatoriamente), vemos que os resultados obtidos são praticamente os mesmos. Uma vantagem deste lab4 em relação ao lab2 foi que já havia o conhecimento prévio dos conceitos de orientação a objeto, bem como alguns padrões que foram utilizados, além do fato de já ter o código escrito em Java. Isso permitiu que o código em Ruby fosse gerado bem mais rápido.
Conclusão
Com este laboratório, foi possível se integrar melhor com a linguagem Ruby, bem como perceber algumas diferenças (até vantajosas) de Ruby em relação a Java. Uma vantagem que foi notável, foi que, devido a sua tipagem dinâmica, excluímos a necessidade da classe abstrata na implementação do padrão Strategy. Isso até faz com que esta solução seja de mais fácil compreensão e evita a necessidade de implementações pouco triviais para contornar restrições da linguagem.
Esqueci de anexar os códigos em ruby utilizados (minhas_classes.rb, simulador.rb e testes.rb), então, estou fazendo isso hoje….