aluno: Francisco Germano Batista da Silva Júnior
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 20/11/2008 (16)
Introdução
O laboratório consistiu na conversão do laboratório 02 feito em Java para Ruby. Todas as classes utilizadas na aplicação, classe main e classe de testes foram traduzidas para a linguagem Ruby. O principal objetivo do laboratório era propiciar uma comparação entre as linguagens, ressaltando as diferenças entre uma linguagem dinâmica/interpretada e uma estática/compilada.
Desenvolvimento
As diferenças de Ruby com relação a Java começam já na estruturação do projeto. Em ruby foram utilizados somente 3 arquivos (um para as classes da aplicação, um arquivo de simulação com comandos para serem executados e um arquivo com os testes), enquanto em Java temos nescessariamente um arquivo para cada uma das classes e um método main para poder rodar o simulador.
Foram criadas 16 classes para a aplicação (sendo uma delas de testes) e o código de simulação.
Aluno
Classe que representa os alunos na aplicação.
class Aluno def initialize (nome, queue) @nome = nome @queue = queue @comportamento = Esforcado.new(self) @conhecimento = 0 end def faz_e_entrega_relatorio() qualidade = (2 * @comportamento.dedicacao() + 1 * @comportamento.inteligencia())/3; originalidade = (1 * @comportamento.dedicacao() + 2 * @comportamento.inteligencia()) / 3; rela = Relatorio.new(qualidade, originalidade, self) @conhecimento += 1 @queue.queue(rela) end attr_reader :conhecimento attr_reader :nome def dedicacao @comportamento.dedicacao end def inteligencia @comportamento.inteligencia end end
AlunoITA
Classe que representa os alunos do ITA.
class AlunoITA < Aluno def comportamento=(comportamento) if(comportamento.instance_of?(Burro)) raise "Comportamento de burro nao permitido para aluno do ITA" end @comportamento=comportamento end end
AlunoUSP
Classe que representa os alunos da USP.
class AlunoUSP < Aluno def comportamento=(comportamento) if(comportamento.instance_of?(Summa)) raise "Comportamento de Summa nao permitido para aluno da USP" elsif(comportamento.instance_of?(SafoPreguicoso)) raise "Comportamento de Safo nao permitido para aluno da USP" end @comportamento=comportamento end end
AlunoUnicamp
Classe que representa os alunos da Unicamp.
class AlunoUnicamp < Aluno def comportamento=(comportamento) if(comportamento.instance_of?(Summa)) raise "Comportamento de Summa nao permitido para aluno da Unicamp" elsif(comportamento.instance_of?(SafoPreguicoso)) raise "Comportamento de Safo nao permitido para aluno da Unicamp" end @comportamento=comportamento end end
Comportamento
Classe que representa o comportamento dos alunos.
class Comportamento def initialize (al) @aluno=al end attr_reader :dedicacao attr_reader :inteligencia end
Esforcado
Classe que representa o comportamento esforçado.
class Esforcado < Comportamento def initialize(al) @dedicacao = 1 @inteligencia = 0.5 @aluno = al end end
Burro
Classe que representa o comportamento burro.
class Burro < Comportamento def initialize (al) @dedicacao=0 @inteligencia=0 @aluno=al end end
Imprevisivel
Classe que representa um comportamento imprevisível.
class Imprevisivel < Comportamento def dedicacao @dedicacao=rand @dedicacao end def inteligencia @inteligencia=rand @inteligencia end end
Pemba
Classe que representa o comportamento de um pemba.
class Pemba < Comportamento def dedicacao @dedicacao = rand/2; @dedicacao end def inteligencia @inteligencia=rand/2; @inteligencia end end
SafoPreguicoso
Classe que representa o comportamento de um aluno safo e preguicoso.
class SafoPreguicoso < Comportamento def initialize(al) @inteligencia=1 @aluno=al end def dedicacao @dedicacao=rand/2; @dedicacao end end
Summa
Classe que representa o comportamento de um aluno que será Summa cum Laude.
class Summa < Comportamento def initialize (al) @inteligencia=1; @dedicacao=1; @aluno=al; end end
Professor
Classe que representa um professor.
class Professor def initialize(fila) @queue = fila end def corrigir_relatorio(rela) @nota = ( rela.qualidade + rela.originalidade + rand) / 3 * 10 end def corrigir_relatorios temp=@queue.dequeue while temp!=nil puts(temp.aluno.nome+ ": " +self.corrigir_relatorio(temp).to_s) temp=@queue.dequeue end end end
Queue
Classe que representa uma fila genérica de objetos.
class Queue def queue(obj) @array ||= [] @array << obj end def dequeue obj = @array.pop return obj; end end
Rela_queue
Sub-classe de Queue que representa uma fila de relatórios.
class Rela_queue < Queue ; end
Relatorio
Classe que representa os relatórios feitos pelos alunos.
class Relatorio def initialize (qld, ori, al) @qualidade=qld @originalidade=ori @aluno=al end attr_reader :qualidade attr_reader :originalidade attr_reader :aluno end
Simulador
Em um arquivo separado das classes foi criado um código para testar e simular a utilização do programa desenvolvido.
require "classes.rb" c = 0 erro = 0 vet_aluno = [] fila = Rela_queue.new() professor = Professor.new(fila) while c != 4 puts "1 - Adicionar aluno" puts "2 - Fazer relatorio" puts "3 - Corrigir relatorios" puts "4 - Sair" begin c=gets() c=c.to_i end while (c < 1 || c > 4) case c when 1 puts "\nDigite o numero de alunos que serao inseridos" n = gets() n=n.to_i range = 1..n n.times do |i| puts "Digite o nome do aluno #{i+1}" str = gets() puts "Digite a instituicao do aluno\n1 - ITA\n2 - USP\n3 - Unicamp\n" begin j=gets() j=j.to_i end while (j < 1 || j > 3) case j when 1 al = AlunoITA.new(str, fila) when 2 al = AlunoUSP.new(str, fila) else al = AlunoUnicamp.new(str, fila) end begin puts "Digite o comportamento do aluno\n1 - Esforcado\n2 - Safo Preguicoso" puts "3 - Pemba\n4 - Suma\n5 - Burro\n6 - Imprevisivel\n" begin j=gets() j=j.to_i end while (j < 1 || j > 6) case j when 1 comp = Esforcado.new(al) begin al.comportamento=(comp) erro = 0 rescue Exception => ex erro = 1 end when 2 comp = SafoPreguicoso.new(al) begin al.comportamento=(comp) erro = 0 rescue Exception => ex puts "Somente alunos do ITA podem ser safos preguicosos\n" erro = 1 end when 3 comp = Pemba.new(al) begin al.comportamento=(comp) erro = 0 rescue Exception => ex erro = 1 end when 4 comp = Summa.new(al) begin al.comportamento=(comp) erro = 0 rescue Exception =>ex puts "Somente alunos do ITA podem ser Summa\n" erro = 1; end when 5 comp = Burro.new(al) begin al.comportamento=(comp) erro = 0 rescue Exception => ex puts "Alunos do ITA nao podem ser burros\n" erro = 1 end when 6 comp = Imprevisivel.new(al); begin al.comportamento=(comp) erro = 0 rescue Exception => ex erro = 1 end end end while (erro == 1) vet_aluno << al end when 2 puts "Qual aluno deve fazer o relatorio?" vet_aluno.length.times do |i| al = vet_aluno[i] puts "#{i+1} - " + al.nome end n = gets() n = n.to_i al = vet_aluno[n - 1] al.faz_e_entrega_relatorio when 3 professor.corrigir_relatorios end puts "\n" end
Abaixo segue o resultado dos testes manuais do programa.
Testes
Para finalizar o laboratório a classe de testes utilizada no laboratório 02 foi convertida para Ruby.
require 'classes' #carregue o código da sua aplicação aqui require 'test/unit' class LabTest < Test::Unit::TestCase def test_funciona_como_o_lab1 #criando os objetos queue = Rela_queue.new prof = Professor.new(queue) # O que muda em termos de interface externa neste lab, # é a criação dos alunos. O que mostra que o nosso código # está bem desacoplado e a nossa refatoração não alterou # o funcionamento dos outros objetos (Professor, Relatório, etc). 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("Nao deveria lançar erro!") end #teste de alunoesforcado 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 relatório do primeiro aluno para investigá-lo rela1 = queue.dequeue assert_equal(aluno1, rela1.aluno) assert(rela1.aluno.nome.eql?("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 coreção dos relas dos 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 = Rela_queue.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 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 < 0.5) #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 != pemba.inteligencia) #deve ser randomica assert(pemba.inteligencia() < 0.5) assert(pemba.dedicacao != pemba.dedicacao) #deve ser randomica assert(pemba.dedicacao < 0.5) end def test_as_sub_classes_de_aluno_tem_restricoes queue = Rela_queue.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(alunoUSP)) fail("Deveria lançar erro!"); rescue Exception => e end begin alunoUnicamp.comportamento=(Summa.new(alunoUnicamp)) fail("Deveria lançar erro!") rescue Exception => e end end def test_rela_queue_eh_subclasse_do_generico_queue q = Rela_queue.new #propriedade básica do polimorfismo end end
Todos os testes foram concluidos com sucesso, comprovando o bom funcionamento do código desenvolvido.
Conclusão
Através desse laboratório foi possível aplicar algumas das diversas diferenças entre Ruby e Java. Ruby sendo uma linguagem dinâmica abre um grande número de possibilidades (devendo o programador possuir especial cuidado com respeito ao polimorfismo de seus objetos), causando até uma estranheza inicial para quem é acostumado a programar em linguagens estáticas. O encapsulamento é uma característica mais presente do que em Java, sendo os comandos de atalho para a criação de acessores a uma variável de instância uma grande vantagem.
Por fim pode-se dizer em linhas gerais que Ruby é uma linguagem mais concisa e legível que Java. O fato da linguagem ser interpretada pode causar complicações para programadores iniciantes pois muitos erros simples que poderiam ser detectados em tempo de compilação só serão detectados no tempo e execução (interpretação da linguagem).
O trabalho desenvolvido não necessitou de um conhecimento profundo de Ruby e não constituiu uma dificuldade na implementação de algoritmos ou padrões de projetos (tudo foi aproveitadod o laborat´roio número 02), entretanto a idéia de comparar o trabalho nas duas linguagens foi extremamente válida para a solidificação dos novos conceitos introduzidos com as linguagens dinâmicas e interpretadas.