5 minute read

Olá, o objetivo deste post é de apresentar as diferentes formas que a linguagem Ruby permite você declarar comportamentos.

Mas o que seria um comportamento? Um comportamento é uma unidade de código (encapsulada dentro de métodos ou funções) que executam uma ação.

Ao longo deste post irei demonstrar como utilizar métodos em diferentes contextos (global, classe, instâncias e módulos) e como utilizar funções.

Obs: Recomendo a leitura desse post caso não saiba como executar os exemplos de código a seguir.

Métodos globais

Métodos globais são métodos acessíveis em qualquer ponto da aplicação, inclusive, essa é a maneira mais simples (e perigosa) de se definir comportamentos em Ruby.

def sum(a, b)
  a + b
end

p sum(1, 2)

p self
p self.class
p self.object_id

Ao executar o código acima, você verá o resultado abaixo (exceto o object_id que será definido para cada execução):

3              # p sum(1, 2)

main           # p self
Object         # p self.class
70335232078200 # p self.object_id

Vamos entender o código acima:

  1. O retorno (output) do método sum(1, 2) foi 3 como esperado.
  2. Ao inspecionar p self (p é um atalho para puts some_object.inspect) obtivemos main [1][2] que para fins práticos podemos assumir como o escopo global.
  3. O resultado de p self.class é Object, ou seja, main é uma instância de Object (o Ruby faz um Object.new assim que o interpretador começar a ler os arquivos .rb do seu programa).
  4. Essa instância como todo objeto em Ruby terá um object_id (endereço na memória), que será único, já que só existirá uma única instância chamada main.

Obs: Escreva no comentário caso queira entender melhor como funciona o self/Ruby Object Model.

Reflexão: Como agrupar comportamentos quando só se usa métodos globais?

Resposta: Aplicando prefixos no nome dos métodos!

Para explicar a resposta acima, imagine o seguinte requisito: adicionar um novo cálculo de multiplicação (multiply) e agrupá-lo a operação de soma (sum). Como foi dito na resposta, podemos prefixar os métodos com um termo em comum (calc_). Ex:

def calc_sum(a, b)
  a + b
end

def calc_multiply(a, b)
  a * b
end

p calc_sum(3, 3)      # 6
p calc_multiply(3, 3) # 9

Nota: Essa forma de se programar é chamada de programação estruturada, ou, programação procedural.

Métodos de classe

Métodos de classe, também conhecidos como métodos estáticos. São métodos que fazem parte da definição de uma classe, ou seja, esses métodos não farão parte dos objetos (instâncias) criados por essas classes.

Como declarar métodos de classe?

Resposta: Usando def self.nome_do_metodo.

Veja um exemplo a seguir.

class Calc
  def self.sum(a, b)
    a + b
  end

  def self.multiply(a, b)
    a * b
  end
end

p Calc.sum(3, 3)      # 6
p Calc.multiply(3, 3) # 9

Perceba que os métodos sum e multiply foram definidos na classe Calc.

Também perceba como essa forma de desenvolver é idêntica a definição de métodos procedurais/globais. Ex:

def calc_sum(a, b)
  a + b
end

class Calc
  def self.sum(a, b)
    a + b
  end
end

Métodos estáticos tem as mesmas propriedades de métodos procedurais. Sendo bem comum que métodos como esses fiquem enormes e se tornem difíceis de manter e testar (será o próximo post do blog).

Métodos de instância

Métodos de instância são métodos que só serão acessíveis ao instanciar uma classe. No Ruby, fazemos isso através do método .new. Ex:

class Calc
  def self.sum(a, b)
    a + b
  end

  def self.multiply(a, b)
    a * b
  end
end

calc = Calc.new

p calc.sum(2, 3)  # Você verá o erro abaixo:
# undefined method `sum' for #<Calc:0x0000...> (NoMethodError)

Pergunta: Por que não foi possível acessar os métodos sum e multiply do objeto calc?
Resposta: Porque os métodos são de classe! 😅

Pergunta: E como transformar esses métodos de classe em métodos de instância?
Resposta: Removendo self da declaração deles! 🙂

Exemplo:

class Calc
  def sum(a, b)
    a + b
  end

  def multiply(a, b)
    a * b
  end
end

calc = Calc.new

p calc.sum(2, 3)      # 5
p calc.multiply(2, 3) # 6

Viu? Funcionou! 😊

Métodos estáticos em módulos

Existe uma outra forma de se ter métodos estáticos em Ruby, essa outra forma é fazendo uso de módulos. Para declarar um módulo, use a palavra module ao invés de class. Ex:

module Calc
  def self.sum(a, b)
    a + b
  end

  def self.multiply(a, b)
    a * b
  end
end

p Calc.sum(3, 3)      # 6
p Calc.multiply(3, 3) # 9

# ---

Calc.new # undefined method `new' for Calc:Module (NoMethodError)

Módulos tem características especiais que não serão retratadas nesse post, mas perceba que:

  1. Foi possível declarar métodos estáticos e fazer uso deles.
  2. Não é possível instanciar um module, sendo essa uma das características que os diferenciam das classes.

Namespaces: Outra característica especial dos módulos

Namespaces é um container capaz de incluir vários tipos de itens como: classes, módulos e constantes. Para exemplificar criarei o module Calc que terá os módulos Sum, Multiply e que responderão a um único método (.call). Ex:

module Calc
  module Sum
    def self.call(a, b)
      a + b
    end
  end

  module Multiply
    def self.call(a, b)
      a * b
    end
  end
end

p Calc::Sum.call(3, 3)      # 6
p Calc::Multiply.call(3, 3) # 9

Pergunta: Sabe o que fizemos ao criar módulos com métodos estáticos que fazem uma única coisa?
Resposta: Uma função!

Mas assim, quanto código para fazer algo tão simples né?

Na minha opinião isso complicou demais. Não seria melhor se Ruby tivesse um suporte real para declarar funções? 🤔

(Spoiler: Ruby tem suporte a funções sim! 🙌)

Funções

Aviso: Existem diferentes formas de se declarar funções em Ruby, mas nesse post apresentarei apenas uma!

Para declarar uma função, utilize a sintaxe -> (args) {}. Ex:

module Calc
  Sum = -> (a, b) { a + b }

  Multiply = -> (a, b) { a * b }
end

p Calc::Sum.call(3, 3)      # 6
p Calc::Multiply.call(3, 3) # 9

Dica: Para saber mais, consulte a doc oficial sobre esse recurso incrível.

Concluindo

Esse post tem como objetivo introduzir os recursos que classificam Ruby como uma linguagem multiparadigma (conforme apresentado na wikipedia), pelo fato dela suportar os paradigmas:

  • Procedural
  • Orientado a objetos
  • Funcional

Pergunta: Tu desconhecia que Ruby tem recursos funcionais? 😱

Sem problemas, confira a palestra (30 minutos) que fiz e que apresenta o quão funcional Ruby é! 😉


Os exemplos de código deste post estão disponíveis nesse gist.

Gostou do conteúdo? Deixe seu comentário aqui embaixo contando o que achou. Valeu! 😉


Já ouviu falar do ada.rb - Arquitetura e Design de Aplicações em Ruby? É um grupo focado em práticas de engenharia de software com Ruby. Acesse o canal no telegram e junte-se a nós em nosso meetup mensal (100% on-line).

Comments