2011-02-10 17 views
167

¿Cómo es que este enfoque de la creación de un método de clase privada de obras:¿Cómo crear un método de clase privado?

class Person 

    def self.get_name 
    persons_name 
    end 

    class << self 

    private 

    def persons_name 
     "Sam" 
    end 
    end 
end 

puts "Hey, " + Person.get_name 
puts "Hey, " + Person.persons_name #=> raises "private method `persons_name' called for Person:Class (NoMethodError)" 

pero esto no significa:

class Person 

    def self.get_name 
    persons_name 
    end 

    private 

    def self.persons_name 
    "Sam" 
    end 
end 

puts "Hey, " + Person.get_name 
puts "Hey, " + Person.persons_name 
+5

Acabo de ver este artículo discutir maneras de crear métodos de clase privada y pensaron que era buena: http://jakeyesbeck.com/2016/01/24/ruby- private-class-methods /?utm_source = rubyweekly & utm_medium = email –

Respuesta

206
no parece funcionar si está definiendo un método en un objeto explícito

private (en su caso self). Puede usar private_class_method para definir los métodos de clase como privados (o como usted describió).

class Person 
    def self.get_name 
    persons_name 
    end 

    def self.persons_name 
    "Sam" 
    end 

    private_class_method :persons_name 
end 

puts "Hey, " + Person.get_name 
puts "Hey, " + Person.persons_name 

alternativa (en Ruby 2.1 +), ya que una definición del método devuelve un símbolo del nombre del método, se puede también utilizar esto como sigue:

class Person 
    def self.get_name 
    persons_name 
    end 

    private_class_method def self.persons_name 
    "Sam" 
    end 
end 

puts "Hey, " + Person.get_name 
puts "Hey, " + Person.persons_name 
39

Por defecto, todos los métodos de clase son públicas. Para que sean privadas puede utilizar como Module#private_class_method @tjwallace escribió o definir de manera diferente, como lo hizo:

class << self 

    private 

    def method_name 
    ... 
    end 
end 

class << self abre clase de singleton de uno mismo, por lo que los métodos pueden ser redefinidos para el objeto de auto actual. Esto se usa para definir el método de clase/módulo ("estático"). Solo ahí, la definición de métodos privados realmente te da métodos privados de clase.

94

ExiRe escribió:

Tal comportamiento de rubí es muy frustrante. Quiero decir, si mueves a la sección privada self.method entonces NO es privado. Pero si lo mueve a la clase < <, entonces de repente funciona. Es simplemente repugnante.

Confundirlo probablemente es frustrante, pero desagradable definitivamente no lo es.

tiene perfecto sentido una vez que entienda modelo de objetos de Ruby y el correspondiente method lookup flow, sobre todo si se tiene en cuenta que private es NO un modificador de acceso/visibilidad, pero en realidad una llamada a un método (con la clase como su receptor) según lo discutido here ... no hay tal cosa como "una sección privada" en Ruby.

Para definir ejemplo métodos privados, se llama a private de la clase de la instancia para establecer la visibilidad por defecto para los métodos definidos posteriormente a privada ... y por lo tanto tiene mucho sentido para definir clase métodos privados llamando private en la clase de la clase, es decir. su metaclass.

Otros corriente principal, lenguajes orientados a objetos autoproclamados le puede dar una sintaxis menos confuso, pero definitivamente el comercio que fuera en contra de un modelo de objeto confuso y menos consistente (inconsistente?) Sin el poder de instalaciones metaprogramación de Ruby.

+0

Entonces, si entiendo correctamente, ruby ​​no tiene acceso a las palabras clave modificadoras (públicas, privadas y protegidas), sino que tiene acceso a los métodos de modificación (público, privado, protegido)? ¿Es esto algo que debería aparecer en el rastreador de errores de ruby ​​para que Matz implemente modificadores de acceso de palabras clave o es este comportamiento esperado? – Edward

+10

@Edward Está _designed_ de esa manera http://junichiito.blogspot.co.uk/2012/03/matz-answers-why-ruby-lets-sub-classes.html. ¿Por qué "apropiado"? – iain

+1

Siguiendo con esto, en lugar de hacer 'private_class_method: method_name' puedes hacer' private_class_method def method_name ... '. – bjt38

4

Yo también, encontrar Ruby (o al menos mi conocimiento de la misma) por debajo de la marca en esta área.Por ejemplo, el siguiente hace lo que yo quiero, pero es torpe,

class Frob 
    attr_reader :val1, :val2 

    Tolerance = 2 * Float::EPSILON 

    def initialize(val1, val2) 
     @val2 = val1 
     @val2 = val2 
     ... 
    end 

    # Stuff that's likely to change and I don't want part 
    # of a public API. Furthermore, the method is operating 
    # solely upon 'reference' and 'under_test' and will be flagged as having 
    # low cohesion by quality metrics unless made a class method. 
    def self.compare(reference, under_test) 
     # special floating point comparison 
     (reference - under_test).abs <= Tolerance 
    end 
    private_class_method :compare 

    def ==(arg) 
     self.class.send(:compare, val1, arg.val1) && 
     self.class.send(:compare, val2, arg.val2) && 
     ... 
    end 
end 

Mis problemas con el código anterior es que los requisitos de sintaxis de Ruby y mi código de métricas de calidad conspiran para hecha para el código engorroso. Para que el código funcione como yo quiero y para silenciar las métricas, debo hacer compare() un método de clase. Como no quiero que forme parte de la API pública de la clase, necesito que sea privada, aunque 'privada' por no funciona. En su lugar, me veo obligado a utilizar 'private_class_method' o algo así. Esto, a su vez, obliga al uso de 'self.class.send (: comparar ...'. Para cada variable pongo a prueba en '==()' Ahora que es un poco difícil de manejar

+0

El hecho de que necesite usar __send__ no tiene nada que ver con el" cómo "marca los métodos de clase private. Los métodos privados no se pueden llamar desde el "exterior". –

9

Sólo por el. exhaustividad, sino que también puede evitar que se declara private_class_method en una línea separada. yo personalmente no me gusta este uso, pero es bueno saber que existe.

private_class_method def self.method_name 
.... 
end 
1

Los métodos de instancia se definen dentro de un bloque de definición de clase. Los métodos de clase se definen como métodos únicos en la clase singleton de una clase, también informalmente conocida como la "metaclase" o "clase de usuario". private no es una palabra clave, sino un método (Module#private).

Esta es una llamada al método self#private/A#private, que "cambia" acceso privado durante todas las próximas definiciones de métodos instancia hasta toggled lo contrario:

class A 
    private 
    def instance_method_1; end 
    def instance_method_2; end 
    # .. and so forth 
end 

Como se señaló anteriormente, los métodos de clase son métodos muy simples definidos en el clase singleton.

def A.class_method; end 

o utilizando una sintaxis especial para abrir el cuerpo de la definición de la clase anónima, Singleton de A:

class << A 
    def class_method; end 
end 

El receptor del "mensaje privado" - auto - dentro class A es el objeto de clase A. self dentro del bloque class << A es otro objeto, la clase singleton.

El siguiente ejemplo es, en realidad, llamando a dos métodos diferentes llamados privado, utilizando dos destinatarios o destinos diferentes para la llamada. En la primera parte, definimos un método de instancia privada ("en la clase A"), en este último definimos un método de clase privado (es de hecho un método singleton en el objeto de clase singleton de A).

class A 
    # self is A and private call "A.private()" 
    private def instance_method; end 

    class << self 
    # self is A's singleton class and private call "A.singleton_class.private()" 
    private def class_method; end 
    end 
end 

Ahora, vuelve a escribir este ejemplo un poco:

class A 
    private 
    def self.class_method; end 
end 

¿Se puede ver el error [que los diseñadores lenguaje Ruby] hicieron? Alterne el acceso privado para todos los métodos de instancia de próxima aparición de A, pero proceda a declarar un método singleton en una clase diferente, la clase singleton.

-5

A partir de rubí 2.3.0

class Check 
    def self.first_method 
    second_method 
    end 

    private 
    def self.second_method 
    puts "well I executed" 
    end 
end 

Check.first_method 
#=> well I executed 
+0

Estaba intentando esto con 'private def self.second_method' antes de cada notación de método, que no funcionaba en mi ruby ​​2.3.3. Pero esta notación funciona para mí. –

+9

Esto es incorrecto, porque llamar 'Check.second_method' también funcionaría sin problemas, por lo que no es realmente privado. – Deiwin

Cuestiones relacionadas