web-dev-qa-db-fra.com

Utilisation de variables d'instance dans les méthodes de classe - Ruby

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
51
tackleberry

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.

70
Steven

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.

22
Frederick Cheung