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