extender-se em rubi

Código de acompanhamento foi testado com rubi 1.9.3 .

Classe é para dados e comportamento

Vejamos este código em rubi.

1classe Util2 def self.double(i)3 i*24 end5end67Util.double(4) #=9762> 8

Aqui temos uma classe Util. Mas note que todos os métodos nesta classe são métodos de classe. Esta classe não tem nenhuma variável de instância. Normalmente uma classe é usada para carregar tanto dados quanto comportamento e, neste caso, a classe Util tem apenas comportamento e nenhum dado.

Ferramentas utilitárias simples em ruby

Agora para obter alguma perspectiva sobre esta discussão vamos olhar para alguns métodos ruby que fazem coisas similares. Aqui estão alguns.

1require 'base64'2Base64.encode64('hello world') #=9762> "aGVsbG8gd29ybGQ=\n "34require 'benchmark'5Benchmark.measure { 10*2000 }67require 'fileutils'8FileUtils.chmod 0644, 'test.rb'910Math.sqrt(4) #=> 2

Em todos os casos acima o método de classe é invocado sem criar uma instância primeiro. Então isto é similar à forma como eu usei Util.double .

No entanto vamos ver qual é a classe de todos estes objetos.

1Base64.class #=> Module2Benchmark.class #=> Module3FileUtils.class #=> Module4Math.class #=> Module

Então estes não são classes mas sim módulos. Isso levanta a questão porque os caras inteligentes do ruby-core os implementaram como módulos ao invés de criar uma classe como eu fiz para o Util.

A razão é que a classe é muito pesada para criar apenas métodos como double. Como discutimos anteriormente, uma classe deve ter tanto dados quanto comportamento. Se a única coisa que lhe interessa é o comportamento então o Ruby sugere implementá-lo como um módulo.

extend self é a resposta

Antes de continuar a discutir extend self aqui está como a minha classe Util irá cuidar depois de passar de Class para Module.

1módulo Util2 extend self34 def double(i)5 i * 26 end7end89puts Util.double(4) #=> 8

Então como extender o self trabalha

Primeiro vamos ver o que extender faz.

1módulo M2 def double(i)3 i * 24 end5end67class Calculator8 extend M9end10puts Calculator.double(4)

No caso acima Calculator está estendendo o módulo M e, portanto, todos os métodos de instância do módulo M estão diretamente disponíveis para Calculator.

Neste caso Calculator é uma classe que estendeu o módulo M. Entretanto Calculator não precisa ser uma classe para estender um módulo.

Agora vamos tentar uma variação onde Calculator é um módulo.

1módulo M2 def double(i)3 i * 24 end5end67módulo Calculadora8 extend M9end10puts Calculadora.double(4) #=> 8

Aqui Calculadora é um módulo que está estendendo outro módulo.

Agora entendemos que um módulo pode estender outro módulo olhar para o código acima e questionar porque o módulo M é mesmo necessário. Por que não podemos mover o método double para o módulo Calculadora diretamente. Vamos tentar isso.

1modulo Calculator2 extend Calculator34 def double(i)5 i * 26 end7end8puts Calculator.double(4) #=> 8

Eu me livrei do módulo M e movi o método double dentro do módulo Calculator. Como o módulo M desapareceu eu mudei de extend M para extend Calculator.

Uma última correção.

Inside the module Calculator what is self. self é o módulo Calculator em si. Então não há necessidade de repetir Calculator duas vezes. Aqui está a versão final

1módulo Calculator2 extend self34 def double(i)5 i * 26 end7end8puts Calculator.double(4) #=9762> 8

Convertendo uma classe em um módulo

Cada vez que eu encontrasse um código como extend self meu cérebro faria uma pausa por um momento. Então eu iria ao Google para ele. Vou ler sobre isso. Três meses depois vou repetir todo o processo.

A melhor maneira de aprender é usando-o. Então eu comecei a procurar um caso para usar extend self. Não é uma boa prática ir à caça de código para aplicar uma idéia que você tem em sua mente, mas aqui eu estava tentando aprender.

Aqui está um instantâneo anterior dos métodos de Util classe que usei em um projeto.

1classe Util2 def self.config2hash(file); end3 def self.in_cents(amount); end4 def self.localhost2public_url(url, protocol); end5end

Após usar extend self code tornou-se

1module Util2 extend self34 def config2hash(file); end5 def in_cents(amount); end6 def localhost2public_url(url, protocol); end7end

Muito melhor. Ele deixa a intenção clara e, acredito, está de acordo com a maneira que o rubi esperaria que usássemos.

Outro uso em linha com a forma como o Rails usa o self

Aqui estou construindo uma aplicação de comércio eletrônico e cada novo pedido precisa obter um novo número de pedido de uma aplicação de vendas de terceiros. O código pode se parecer com isto. Eu omiti a implementação dos métodos porque eles não são relevantes para esta discussão.

1classe Order2 def amount; end3 def buyer; end4 def shipped_at; end5 def number6 @number || self.class.next_order_number7 end89 def self.next_order_number; 'A100'; end10end1112puts Order.new.number #=> A100

Here o método next_order_number pode estar fazendo uma chamada complicada para outro sistema de vendas. O ideal seria que a classe Order não expusesse o método next_order_number . Assim podemos fazer este método private mas isso não resolve o problema raiz. O problema é que o modelo Order não deveria saber como o novo número de pedido é gerado. Bem, podemos mover o método next_order_number para outra classe Util mas isso criaria demasiada distância.

Aqui está uma solução usando extend self.

1módulo Checkout2 extend self34 def next_order_number; 'A100'; end56 class Order7 def amount; end8 def buyer; end9 def shipped_at; end10 def number11 @number ||| Checkout.next_order_number12 end13 end14end1516puts Checkout::Order.new.number #=> A100

Muito melhor. A classe Order não está expondo o método next_order_number e este método está bem ali no mesmo arquivo. Não é necessário abrir a classe Util classe.

.

Articles

Deixe uma resposta

O seu endereço de email não será publicado.