24 Jan 2016
Na linguagem de programação Ruby, métodos definidos vem em duas variantes: métodos de instância e métodos de classe. Métodos de instância estão disponíveis após um objeto ter sido inicializado, criando uma instância. Métodos de classe, por outro lado, estão disponíveis sem criar uma instância da classe em que são definidos.
os métodos de Ruby podem variar na visibilidade. Os métodos públicos estão disponíveis em qualquer contexto, enquanto a disponibilidade dos métodos privados é restringida dentro da instância de uma classe e seus descendentes. Um terceiro escopo de visibilidade, protegido, se comporta de forma semelhante a métodos privados, mas métodos protegidos podem ser chamados por outras instâncias da mesma classe.
Para uma rápida atualização, públicas e privadas, métodos de instância olhar como este:
class Dog def do_trick bark end private def bark puts 'woof woof' endend
Quando o público é chamado de método:
> dog = Dog.new> dog.do_trick# => woof woof
E o método privado:
> dog = Dog.new> dog.bark# => NoMethodError: private method `bark' called for <Dog>
os métodos de classe privada podem não ser tão comuns como os métodos de instância privada, mas eles ainda têm o seu lugar. Por exemplo, um método de classe pode exigir métodos auxiliares internos para completar sua função. Seja qual for a razão, definir métodos de classe privada tem valor, mas nem sempre é intuitivo.
este exemplo Dog
Classe precisa manter uma lista de tricks
que será usado dentro dos outros métodos de classe pública. Esta lista não deve ser acessível a qualquer chamador fora da classe Dog
.
O caminho errado
Um primeiro passo no escrever o privado tricks
método pode parecer como:
class Dog private def self.tricks endend
no Entanto, ao testar a visibilidade do tricks
método:
> Dog.tricks# =>
Uh oh, nenhum erro foi lançada indicando que um particular método foi chamado, este método é totalmente público.Porquê?
o motivo que O código acima não produzir um método privado tem a ver com Ruby hierarquia de objetos, interações entre interno classes, instâncias dessas classes, e eigenclasses. Uma escrita detalhada sobre como os objetos de Ruby trabalham uns com os outros pode ser encontrada em um post anterior.
ao definir métodos em uma classe, o contexto padrão e o contexto introduzido pela declaração do método self.
são distintos.
o âmbito do método privado não pode ser utilizado da forma acima descrita, uma vez que não lida com a mudança de contexto entre os métodos definidos na classe Dog
e definidos no contexto self
.Quais são as alternativas?
classe << auto
Uma forma alternativa de definir métodos de classe em um objeto é usar o class << self
sintaxe. Esta sintaxe abre um eigenclass para o argumento fornecido. Neste exemplo, self
dentro da classe Dog
abrirá o Dog's
eigenclass.
class Dog class << self private def tricks end endend
uma coisa a notar é que ao definir métodos como este, a declaração mudou de def self.tricks
para simplesmente def tricks
.
actualmente, o âmbito privado é preservado e o comportamento esperado é alcançado:
> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
Módulos
Módulos em ruby são coleções de métodos e constantes. Estas coleções podem ser usadas como ferramentas de encapsulação ou, neste caso, alternativas à definição de métodos de classe pública e privada.
quando uma classe extends
um módulo, todos os métodos dentro desse módulo tornam-se Métodos de classe na classe de assunto*. Este padrão pode ser usado para definir o método tricks
em Dog
.
*Nota: Isto só diz respeito a métodos definidos em um “sentido típico” qualquer definição de método def self.
ou def Dog.
em um módulo não se tornam automaticamente métodos de classe da mesma forma quando extended
.
class Dog module ClassMethods private def tricks end end extend ClassMethodsend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
uma vantagem desta abordagem é a legibilidade. O módulo chamado ClassMethods
, que está contido e, portanto, encapsulado pela classe Dog
, é o lar claro para todos os métodos de classe desejados.
modificadores de visibilidade como private
comportem-se de acordo e um simples extend
na parte inferior da definição do módulo reúne tudo.
private_class_metod
uma terceira abordagem é usar o método construído no método Module#private_class_method
. O objetivo deste método é Alterar a visibilidade dos métodos de classe existentes.
ao contrário das outras duas soluções, este não exige um tipo de método de definição incorrecta solução:
class Dog def self.tricks end private_class_method :tricksend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
Este método também pode ser forrado durante a aula de’ definição do método:
class Dog private_class_method def self.tricks endend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
Se um único método de classe deve ser privado e de economia de linhas é muito importante, esse estilo pode ser aplicável; mas, certamente sacrifica um nível de legibilidade.
uma grande força de private_class_method
é a sua explicitação. Ao contrário das outras abordagens, esta não requer uma definição especial de módulo ou mudança de contexto de definição de método.
Leitura Adicional
as soluções acima mencionadas irão fazer o trabalho, mas poderão não responder a perguntas persistentes sobre por que as coisas funcionam como funcionam. Alguns grandes artigos sobre o ruby eigenclass e pensamentos de Matz sobre o design do método Ruby deve ajudar a pintar uma imagem mais concisa de Por Que A definição de método da classe privada Ruby é complexa.