A desvendar Classes, instâncias e Metaclasses em Ruby

bem-vindos a um novo episódio de Ruby Magic! A edição deste mês é sobre metaclasses, um assunto suscitado por uma discussão entre dois desenvolvedores (Hi Maud!).Através da análise de metaclasses, aprenderemos como os métodos de classe e instância funcionam em Ruby. Ao longo do caminho, descubra a diferença entre definir um método passando um “definee” explícito e usando class << self ou instance_eval. Vamos!

Classe instâncias e métodos de instância

para entender por que metaclasses são usados em Ruby, vamos começar por examinar quais são as diferenças entre instância – e métodos de classe.

em Ruby, uma classe é um objeto que define um projeto para criar outros objetos. Classes definem quais métodos estão disponíveis em qualquer instância dessa classe.

definir um método dentro de uma classe cria um método de instância nessa classe. Qualquer instância futura dessa classe terá esse método disponível.

1 2 3 4 5 6 7 8 9 10 11 12
class User def initialize(name) @name = name end def name @name end end user = User.new('Thijs') user.name # => "Thijs" 

neste exemplo, vamos criar uma classe chamada User, com um método de instância chamado #name que retorna o nome do usuário. Usando a classe, então criamos uma instância de classe e a armazenamos em uma variável chamada user. Uma vez que user é uma instância da classe User, tem o método #name disponível.

uma classe armazena os seus métodos de instância na sua tabela de métodos. Qualquer instância dessa classe refere-se à sua tabela de método de classe para ter acesso aos seus métodos de instância.

Classe de Objetos

Um método de classe é um método que pode ser chamado diretamente na classe sem ter que criar uma instância primeira. Um método de classe é criado por prefixação de seu nome com self. ao defini-lo.

uma classe é ela mesma um objeto. Uma constante se refere ao objeto de classe, então métodos de classe definidos nele podem ser chamados de qualquer lugar na aplicação.

1 2 3 4 5 6 7 8 9
class User # ... def self.all end end User.all # => 

Métodos definidos com um self.-prefixo será adicionado à classe o método da tabela. Em vez disso, são adicionados ao metaclass da classe.

Metaclasses

aparte de uma classe, cada objeto em Ruby tem um metaclasse escondido. Metaclasses são singletons, o que significa que pertencem a um único objeto. Se você criar várias instâncias de uma classe, eles vão compartilhar a mesma classe, mas todos eles terão metaclasses separados.

1 2 3 4 5 6 7 8 9
thijs, robert, tom = User.all thijs.class # => User robert.class # => User tom.class # => User thijs.singleton_class # => #<Class:#<User:0x00007fb71a9a2cb0>> robert.singleton_class # => #<Class:#<User:0x00007fb71a9a2c60>> tom.singleton_class # => #<Class:#<User:0x00007fb71a9a2c10>> 

neste exemplo, podemos ver que, embora cada um dos objetos tem a classe User, suas singleton classes têm diferentes Identificações de objeto, o que significa que eles são objetos separados.Ao ter acesso a um metaclass, Ruby permite adicionar métodos diretamente aos objetos existentes. Fazê-lo não irá adicionar um novo método à classe do objecto.

1 2 3 4 5 6 7 8
robert = User.new("Robert") def robert.last_name "Beekman" end robert.last_name # => "Beekman" User.new("Tom").last_name # => NoMethodError (undefined method `last_name' for #<User:0x00007fe1cb116408>) 

neste exemplo, vamos adicionar um #last_name para o usuário armazenado na robert variável. Embora robert seja uma instância de User , qualquer instância recém-criada de User não terá acesso ao método #last_name, uma vez que só existe no metaclass de robert.

o que é o self?

ao definir um método e passar por um receptor, o novo método é adicionado ao metaclass do receptor, em vez de adicioná-lo à tabela de método da classe.

1 2 3 4 5
tom = User.new("Tom") def tom.last_name "de Bruijn" end 

No exemplo acima, adicionamos #last_name diretamente na tom objeto, passando tom como o receptor, ao definir o método.

é também assim que funciona para métodos de classe.

1 2 3 4 5 6 7
class User # ... def self.all end end 

Aqui, vamos passar explicitamente self como um receptor ao criar o .all método. Numa definição de classe, self refere-se à classe (User neste caso), de modo que o método .all é adicionado ao metaclasse de User.

Because User is an object stored in a constant, we’ll access the same object-and the same metaclass-whenever we reference it.

abrindo o Metaclass

aprendemos que métodos de classe são métodos no metaclass do objeto de classe. Sabendo isso, vamos olhar para algumas outras técnicas de criação de métodos de classe que você pode ter visto antes.

classe < < self

embora tenha saído um pouco do estilo, algumas bibliotecas usam class << self para definir métodos de classe. Este truque de sintaxe Abre o metaclass da classe atual e interage diretamente com ele.

1 2 3 4 5 6 7 8 9 10 11
class User class << self self # => #<Class:User> def all end end end User.all # => 

Este exemplo cria um método de classe chamado User.all adicionando um método para User‘s metaclasse. Em vez de passar explicitamente um receptor para o método como vimos anteriormente, ajustamos o metaclass self para Userem vez do próprio User.

como aprendemos Antes, qualquer definição de método sem um receptor explícito é adicionado como um método de instância da classe atual. No interior do bloco, A classe actual é o metaclass User(#<Class:User>).

instance_eval

outra opção é usar instance_eval , que faz a mesma coisa com uma diferença maior. Embora o metaclass da classe receba os métodos definidos no bloco, self permanece uma referência à classe principal.

1 2 3 4 5 6 7 8 9 10 11
class User instance_eval do self # => User def all end end end User.all # => 

neste exemplo, vamos definir um método de instância em User‘s metaclasse como antes, mas self ainda aponta para User. Embora normalmente aponte para o mesmo objeto, o” default definee ” e self pode apontar para objetos diferentes.

o Que Aprendemos

aprendemos que as classes são os únicos objetos que podem ter métodos, e que métodos de instância são, na verdade, métodos de um objeto metaclasse. Sabemos que class << self simplesmente troca self ao redor para permitir que você defina métodos no metaclass, e sabemos que instance_eval faz principalmente a mesma coisa (mas sem tocar self).Apesar de não trabalhar explicitamente com metaclasses, Ruby usa-os extensivamente sob o capô. Saber o que acontece quando você define um método pode ajudá-lo a entender por que Ruby se comporta como ele faz (e por que você tem que prefixar métodos de classe com self.).Obrigado pela leitura. Se você gostou do que você leu, Você pode gostar de subscrever a Ruby Magic para receber um e-mail quando publicamos um novo artigo sobre uma vez por mês.



+