quinta-feira, 1 de agosto de 2013

Ruby: Orientação a objetos em detalhes

Olá, pessoal! 
De fato, a linguagem ganhou bastante destaque no cenário mundial graças ao framework Ruby on Rails, e por esse mesmo motivo percebo que a grande maioria dos desenvolvedores iniciam o aprendizado sobre Rails e Ruby ao mesmo tempo. Com isso, alguns pontos importantes da linguagem são “aprendidos” de forma secundária, sem um entendimento mais aprofundado sobre como as coisas realmente funcionam.
Por esse motivo, vou iniciar a série falando sobre Orientação a Objetos em Ruby. Não vamos, aqui, abordar o paradigma de Orientação a Objetos em si, mas sim algumas particularidades da linguagem.
Nesse contexto, uma das frases mais famosas é: “Em Ruby tudo é objeto”.
Mas o que isso realmente significa?
Significa que até mesmo uma classe é um objeto. Nesse caso, uma classe é um objeto da classe Class. O principal para entendermos essa afirmação está relacionado com o fato de que Ruby é uma linguagem interpretada e as definições de nossos programas são literalmente executadas. Logo, quando definimos uma classe, estamos na verdade criando uma instância da classe Class e, com isso, para entendermos de forma mais clara o funcionamento do modelo de objetos em Ruby, é necessário pensarmos em função dos objetos, e não das classes.
Um exemplo: podemos definir uma simples classe em Ruby da seguinte forma:
class Person
end
p = Person.new
Quando esse código for interpretado, um objeto do tipo Class será criado e associado com uma constante global, que no caso é “Person”.
Vejam que pelo simples fato de escrevermos class Person end já estaremos criando um objeto. Quando fazemos Person.new, estamos criando um objeto Person, e o método new do objeto Class é executado por padrão.
Uma outra frase famosa: “Ruby é uma linguagem 100% orientada a objetos”.
Isso quer dizer que, diferentemente da maioria das linguagens, não temos construções como number = Math.abs(number), onde um método separado é chamado. Esse é um código Java onde o static method abs da classe Math é chamado para poder calcular o valor absoluto do número passado como
parâmetro.
Em Ruby, a responsabilidade de determinar o valor absoluto de um número pertence aos próprios números. Logo, podemos ter algo como: number = number.abs. Nesse caso, podemos dizer que estamos enviando a mensagem “abs” para o objeto number.
Uma nova frase famosa: “Em Ruby as classes são abertas”.
Isso significa que, ao contrário da maioria das outras linguagens de programação, podemos “injetar” códigos para modificar qualquer classe. Pelo dinamismo da linguagem, isso acontece em tempo de execução, e essa é uma das funcionalidades que mais tornam a linguagem poderosa. Não só podemos injetar códigos, mas também modificar comportamentos.
Vamos a um exemplo prático. Iremos definir uma classe com apenas um método e logo abaixo iremos acrescentar um novo.
class Animal
  def speak
    puts "speaking..."
  end
end

class Animal
  def walk
    puts "walking..."
  end
end
No segundo trecho, é como se nós estivéssemos “abrindo” a classe Animal e injetando um novo método.
class Animal
  def speak
    puts "Speaking"
  end
end

def walk
  puts "Walking"
end

Animal.send(:public, :walk)
No caso acima, utilizar o “send” permite-nos criar objetos da classe Animal que respondem à mensagem “walk”.
Uma dúvida que talvez possa surgir: o que o método walk está fazendo “perdido” no exemplo acima? Na realidade ele não está “perdido”. Ele já está dentro de um objeto chamado main, que no caso é uma instância da classe Object.
Continuando os exemplos de “classes abertas”, em um dos exemplos acima foi citado que em uma instrução do tipo Person.new, o método new do objeto Class é executado por padrão. Se Class é um objeto da classe Class, podemos “abri-la” e também alterar seu respectivo funcionamento.
Vejam um exemplo:
class Class
alias oldNew new
def new(*args)
print "Creating a new ", self.name, "\n"
oldNew(*args)
end
end

class Name
end

n = Name.new #Creating a new Name
Nesse trecho de código, primeiramente “abrimos” a classe Class e criamos um alias (apelido) para o método new. Agora ele se chama oldNew. Tecnicamente falando, o método de classe alias cria um novo método e o aponta para a implementação do método antigo.
Em seguida, definimos o “nosso” método new e inserimos um simples print, indicando que estamos criando um novo objeto. Por último, realizamos a chamada do “verdadeiro” método new passando os mesmos argumentos, para que ele possa alocar espaço para o objeto.
Com isso é como se estivéssemos “sobrescrevendo” o método new.
Quando a instrução n = Name.new for executada, deveremos ver a mensagem “creating a new Name” sendo exibida.
Relembrando que tudo é objeto.
Para finalizar, vamos verificar uma simples atribuição do tipo.
number = 7 + 3  
Algumas pessoas se assustam ao saber que nesse caso o = (igual) na verdade é um método. Nesse exemplo da atribuição acontece o seguinte:
  1. O + (mais) é um método do objeto 7, e este é uma instância da classe Fixnum.
  2. 3 também é uma instância da classe Fixnum e é passada como parâmetro para o método + (mais)
  3. O resultado (que é uma instância da classe Fixnum) é atribuído para o objeto “number”.
Vamos a um outro exemplo mais detalhado.
Vejamos esta instrução:
Person.name = "My new name!" 
Aparentemente estamos acessando o atributo “name” da classe Person e atribuindo um novo valor. Correto?
Não. A classe Person possui este método(name=) que permite atualizar o valor. Ruby irá interpretar a chamada da seguinte forma:
Person.nome=("My new name!") 
Percebam que na verdade o método se chama name= e recebe como parâmetro uma String como o novo nome. Logo, em Ruby não é possível acessar diretamente os atributos de uma classe.
Em meu próximo artigo irei abordar outras peculiaridades da linguagem para que em breve possamos falar de um assunto bastante discutido na comunidade Ruby: Metaprogramação!
Até a próxima!

FONTE: iMasters

Nenhum comentário: