J'ai une classe quelque chose comme ci-dessous, et j'ai utilisé des variables d'instance (tableau) pour éviter d'utiliser beaucoup de paramètres de méthode.
Cela fonctionne comme je m'y attendais, mais est-ce une bonne pratique? En fait, je ne m'attendrais pas à ce que cela fonctionne, mais je suppose que les méthodes de classe ne fonctionnent pas comme des méthodes statiques dans d'autres langages.
class DummyClass
def self.dummy_method1
@arr = []
# Play with that array
end
def self.dummy_method2
# use @arr for something else
end
end
La raison pour laquelle les variables d'instance fonctionnent sur les classes dans Ruby est que Ruby classes sont les instances elles-mêmes (instances de classe Classe ). Essayez-le par vous-même en inspectant DummyClass.class
. Il n'y a pas de "méthodes statiques" au sens C # dans Ruby car chaque méthode est définie sur (ou héritées dans) une instance et invoquées dans une instance. Par conséquent, elles peuvent accéder à toutes les variables d'instance disponibles sur l'appelé.
Puisque DummyClass
est une instance, elle peut très bien avoir ses propres variables d'instance. Vous pouvez même accéder à ces variables d'instance tant que vous avez une référence à la classe (qui devrait toujours être parce que les noms de classe sont des constantes). À tout moment, vous pourriez appeler ::DummyClass.instance_variable_get(:@arr)
et obtenir la valeur actuelle de cette variable d'instance.
Quant à savoir si c'est une bonne chose à faire, cela dépend des méthodes .
Si @arr
Est logiquement l '"état" de l'instance/classe DummyClass
, stockez-le dans la variable d'instance. Si @arr
N'est utilisé que dans dummy_method2
Comme raccourci opérationnel, passez-le comme argument. Pour donner un exemple où l'approche de variable d'instance est utilisée, considérez ActiveRecord dans Rails. Cela vous permet de faire ceci:
u = User.new
u.name = "foobar"
u.save
Ici, le nom qui a été attribué à l'utilisateur est des données qui se trouvent légitimement sur l'utilisateur. Si, avant l'appel #save
, On demandait "quel est le nom de l'utilisateur à ce stade", vous répondriez "foobar". Si vous creusez assez loin dans les internes (vous creuserez très loin et dans beaucoup de métaprogrammation, vous constaterez qu'ils utilisent des variables d'instance pour exactement cela).
L'exemple que j'ai utilisé contient deux invocations publiques distinctes. Pour voir un cas où les variables d'instance sont toujours utilisées malgré un seul appel, consultez l'implémentation ActiveRecord de #update_attributes
. Le corps de la méthode est simplement load(attributes, false) && save
. Pourquoi #save
Ne reçoit-il aucun argument (comme le nouveau name
) même s'il se trouve dans le corps de la sauvegarde où quelque chose comme UPDATE users SET name='foobar' WHERE id=1;
? C'est parce que des choses comme le nom sont des informations qui appartiennent à l'instance.
Inversement, nous pouvons examiner un cas où les variables d'instance n'auraient aucun sens à utiliser. Regardez l'implémentation de #link_to_if
, une méthode qui accepte un argument booléen (généralement une expression dans le code source) à côté d'arguments généralement acceptés par #link_to
comme l'URL vers laquelle créer un lien. Lorsque la condition booléenne est vraie, elle doit passer le reste des arguments à #link_to
Et l'invoquer. Cela n'aurait pas beaucoup de sens d'affecter des variables d'instance ici parce que vous ne diriez pas que le contexte d'appel ici (le moteur de rendu) contient ces informations dans l'instance. Le moteur de rendu lui-même n'a pas d '"URL vers laquelle se lier" et, par conséquent, il ne doit pas être enterré dans une variable d'instance.
Ce sont des variables d'instance de classe et sont des choses parfaitement légitimes dans Ruby: les classes sont aussi des objets (instances de classe) et ont donc des variables d'instance.
Une chose à surveiller est que chaque sous-classe aura son propre ensemble de variables d'instance de classe (après tout ce sont des objets différents): Si vous avez sous-classé DummyClass
, les méthodes de classe de la sous-classe ne pourraient pas voir @arr
.
Variables de classe (@@foo
) sont bien sûr l'inverse: toute la hiérarchie des classes partage les mêmes variables de classe.