web-dev-qa-db-fra.com

Comment créer une méthode de classe privée?

Comment se fait-il que cette approche de création d'une méthode de classe privée fonctionne:

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)"

Mais cela ne veut pas:

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
204
99miles

private ne semble pas fonctionner si vous définissez une méthode sur un objet explicite (dans votre cas, self). Vous pouvez utiliser private_class_method pour définir les méthodes de classe comme étant privées (ou similaires à celles que vous avez décrites).

_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
_

Alternativement (dans Ruby 2.1+), puisqu’une définition de méthode renvoie un symbole du nom de la méthode, vous pouvez également l’utiliser comme suit:

_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
_
251
tjwallace

ExiRe a écrit:

Un tel comportement de Ruby est vraiment frustrant. Je veux dire si vous passez à la section privée self.method alors ce n’est PAS privé. Mais si vous le déplacez en classe << auto, alors cela fonctionne soudainement. C'est dégoûtant.

Cela peut être déroutant, frustrant, mais dégoûtant.

Cela prend tout son sens une fois que vous avez compris le modèle objet de Ruby et le flux de recherche de méthode , en particulier si vous prenez en compte le fait que private est PAS un modificateur d'accès/visibilité, mais en réalité un appel de méthode (avec la classe comme destinataire) comme indiqué ici .. . "une section privée" en Ruby.

Pour définir des méthodes d'instance privées , appelez private sur la classe de l'instance pour définir la visibilité par défaut des méthodes définies ultérieurement sur privé ... et il est donc parfaitement logique de définir des méthodes privées class en appelant private sur la classe de la classe, c'est-à-dire. sa métaclasse.

D'autres langages traditionnels auto-proclamés OO peuvent vous donner une syntaxe moins déroutante, mais vous optez pour un échange qui le rend moins confus et moins cohérent (incohérent?) modèle d'objet sans la puissance des installations de métaprogrammation de Ruby.

103
pvandenberk

Par défaut, toutes les méthodes de classe sont publiques. Pour les rendre privés, vous pouvez utiliser Module # private_class_method comme @tjwallace les a écrits ou les définir différemment, comme vous l'avez fait:

class << self

  private

  def method_name
    ...
  end
end

class << self ouvre la classe singleton de self, ce qui permet de redéfinir les méthodes pour l'objet self actuel. Ceci est utilisé pour définir la méthode class/module ("static"). Seulement là, la définition de méthodes privées vous donne vraiment des méthodes de classes privées.

66
roxxypoxxy

Juste pour être complet, nous pouvons également éviter de déclarer private_class_method sur une ligne séparée. Personnellement, je n'aime pas cet usage, mais bon de savoir qu'il existe.

private_class_method  def self.method_name
 ....
end
12
Emre Basala

Moi aussi, trouver Ruby (ou du moins ma connaissance de ce sujet) est à la limite de la marque dans ce domaine. Par exemple ce qui suit fait ce que je veux mais est maladroit,

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

Mon problème avec le code ci-dessus est que les exigences de syntaxe Ruby et les métriques de qualité de code conspirent pour rendre le code encombrant. Pour que le code fonctionne à la fois comme je le veux et pour calmer les métriques, je dois faire de compare () une méthode de classe. Puisque je ne veux pas que cela fasse partie de l'API publique de la classe, j'ai besoin que ce soit privé, mais "privé" en soi ne fonctionne pas. Au lieu de cela, je suis obligé d'utiliser 'private_class_method' ou une solution de ce type. Ceci, à son tour, force l'utilisation de 'self.class.send (: compare ...') pour chaque variable que je teste dans '== ()'. Voilà qui est un peu difficile à manier.

4
dinman2022

Les méthodes d'instance sont définies dans un bloc de définition de classe. Les méthodes de classe sont définies comme des méthodes singleton sur la classe singleton d'une classe, également appelée "métaclasse" ou "classe propre". private n'est pas un mot clé, mais une méthode ( Module # private ).

Ceci est un appel à la méthode self#private/A#private qui "active" l’accès privé à toutes les définitions de méthode d’instance à venir jusqu’à basculement:

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

Comme indiqué précédemment, les méthodes de classe sont en réalité des méthodes singleton définies sur la classe singleton.

def A.class_method; end

Ou en utilisant une syntaxe spéciale pour ouvrir le corps de définition de la classe singleton, anonyme, de A:

class << A
  def class_method; end
end

Le destinataire du "message privé" - self - à l'intérieur de class A est l'objet de classe A. self à l'intérieur du bloc class << A. est un autre objet, la classe singleton.

L'exemple suivant appelle en réalité deux méthodes différentes appelées privées , utilisant deux destinataires ou cibles différents pour l'appel. Dans la première partie, nous définissons une méthode d'instance privée ("on class A"), dans la dernière nous définissons une méthode de classe privée (c'est en fait une méthode singleton sur l'objet de classe 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

Maintenant, réécrivez un peu cet exemple:

class A
  private
    def self.class_method; end
end

Pouvez-vous voir l'erreur [que les Ruby designers de langage] ont faite? Vous basculez sur l'accès privé pour toutes les méthodes d'instance à venir de A, mais déclarez une méthode singleton sur une classe différente, la classe singleton.

3
Martin Andersson