Ruby: Entendendo Classes, Objetos e Variáveis

Ruby é uma linguagem dinâmica, elegante e poderosa, conhecida por sua sintaxe limpa e expressiva. Um dos aspectos mais importantes de Ruby é seu modelo de programação orientada a objetos, que permite estruturar códigos de forma intuitiva e eficiente. Neste artigo, vamos explorar os fundamentos de classes, objetos e variáveis em Ruby, desvendando a simplicidade e potência desses conceitos.

A Base da Orientação a Objetos em Ruby

Em Ruby, tudo é um objeto. Esta não é apenas uma afirmação retórica - é um princípio fundamental da linguagem. Desde números e strings até as próprias classes, tudo em Ruby é instanciado a partir de uma classe. Esta característica proporciona uma consistência valiosa para quem está aprendendo a linguagem.

Definindo Classes

Vamos começar com um exemplo prático. Imagine que estamos gerenciando uma livraria de livros usados. Precisamos desenvolver um sistema que realize o controle de estoque, processando arquivos CSV que contêm informações como ISBN e preço dos livros.

Uma boa abordagem para design orientado a objetos é identificar os conceitos do domínio. No nosso caso, um conceito fundamental é o livro em estoque. Podemos representá-lo com uma classe:

class BookInStock
  def initialize(isbn, price)
    @isbn = isbn
    @price = Float(price)
  end
end

Esta simples declaração já nos permite criar instâncias:

a_book = BookInStock.new("isbn1", 3)
another_book = BookInStock.new("isbn2", 3.14)

O Método Initialize

O método initialize é especial em Ruby. Quando você chama BookInStock.new, Ruby aloca memória para um objeto não inicializado e então chama o método initialize desse objeto, passando todos os argumentos que foram passados para new.

Isso permite configurar o estado inicial do objeto. No nosso exemplo, o método initialize transfere os parâmetros para as variáveis de instância @isbn e @price. Note o símbolo @ - ele identifica variáveis de instância em Ruby, que são armazenadas com cada objeto e acessíveis em todos os métodos desse objeto.

Objetos e Atributos

As variáveis de instância são encapsuladas nos objetos - são privadas por padrão. Isso é vantajoso, pois garante que o objeto seja responsável por manter sua própria consistência. Porém, precisamos definir métodos que permitam ao mundo exterior interagir com o objeto. Esses aspectos visíveis externamente são chamados de atributos.

Métodos de Acesso

Ruby facilita a criação de métodos acessores através de atalhos convenientes:

class BookInStock
  attr_reader :isbn, :price  # Cria métodos de leitura
  attr_accessor :price       # Cria métodos de leitura e escrita
  
  def initialize(isbn, price)
    @isbn = isbn
    @price = Float(price)
  end
end

O método attr_reader cria métodos para leitura, enquanto attr_accessor cria métodos para leitura e escrita. Isso permite acessar atributos de forma simples e elegante:

book = BookInStock.new("isbn1", 33.80)
puts "ISBN = #{book.isbn}"        # Lendo um atributo
book.price = book.price * 0.75    # Modificando um atributo

Atributos São Apenas Métodos

Um conceito fundamental em Ruby é que atributos são apenas métodos sem argumentos. Eles não necessariamente precisam apenas retornar ou atribuir valores a variáveis de instância. Você pode implementar lógica mais complexa:

def price_in_cents
  (price * 100).round
end

def price_in_cents=(cents)
  @price = cents / 100.0
end

Isso cria uma variável de instância "virtual". Para o mundo exterior, price_in_cents parece ser um atributo como qualquer outro, mas internamente não possui variável de instância correspondente.

Este princípio, conhecido como "Princípio do Acesso Uniforme", é poderoso - você oculta a implementação do resto do mundo, tendo liberdade para alterá-la no futuro sem impactar o código que utiliza sua classe.

Classes Trabalhando Juntas

Resolver problemas complexos geralmente envolve múltiplas classes trabalhando juntas. No nosso exemplo da livraria, além da classe BookInStock, podemos criar uma classe CsvReader para processar os arquivos CSV:

class CsvReader
  def initialize
    @books_in_stock = []
  end
  
  def read_in_csv_data(csv_file_name)
    CSV.foreach(csv_file_name, headers: true) do |row|
      @books_in_stock << BookInStock.new(row["ISBN"], row["Price"])
    end
  end
  
  def total_value_in_stock
    sum = 0.0
    @books_in_stock.each { |book| sum += book.price }
    sum
  end
end

Esta abordagem de dividir responsabilidades entre classes torna o código mais modular, legível e manutenível.

Controle de Acesso

Ruby oferece três níveis de controle de acesso para métodos:

  1. Public: Métodos acessíveis por qualquer código (padrão)
  2. Protected: Métodos acessíveis apenas por objetos da mesma classe ou subclasses
  3. Private: Métodos que só podem ser chamados no contexto do objeto atual

Isso permite controlar quanta exposição sua classe terá ao mundo exterior:

class MyClass
  def method1
    # Método público
  end
  
  protected def method2
    # Método protegido
  end
  
  private def method3
    # Método privado
  end
end

Variáveis e Referências

Em Ruby, variáveis não são objetos - são referências a objetos. Os objetos existem em um "pool" (geralmente a heap do sistema operacional) e são apontados por variáveis. Isso tem implicações importantes:

person1 = "Tim"
person2 = person1    # Ambos apontam para o mesmo objeto
person1[0] = 'J'     # Modifica o objeto
puts person2         # Exibe "Jim", não "Tim"

Se quiser evitar esse comportamento, você pode usar dup para criar uma cópia do objeto, ou freeze para impedir modificações.

Reabrindo Classes

Uma característica única de Ruby é a capacidade de reabrir uma definição de classe e adicionar novos métodos ou variáveis a qualquer momento, mesmo para classes da biblioteca padrão:

class String
  def reverso_e_maiusculo
    reverse.upcase
  end
end

puts "teste".reverso_e_maiusculo  # Imprime "ETSET"

Essa técnica, conhecida como "monkey patching", pode ser poderosa, mas deve ser usada com cautela para evitar comportamentos imprevisíveis.

Conclusão

O modelo de objetos em Ruby é simples, consistente e flexível, tornando a linguagem uma excelente escolha para programação orientada a objetos. A sintaxe expressiva e os atalhos convenientes permitem escrever código limpo e elegante, enquanto os conceitos sólidos de OO proporcionam uma base robusta para aplicações complexas.

Se você está começando com Ruby ou buscando aprofundar seus conhecimentos em programação orientada a objetos, explorar esses conceitos fundamentais será um passo valioso na sua jornada de aprendizado.

Comentários (0)

Nenhum comentário:

Postar um comentário