web-dev-qa-db-fra.com

Comment utilisez-vous des variables globales ou des valeurs constantes dans Ruby?

J'ai un programme qui ressemble à:

$offset = Point.new(100, 200);

def draw(point)
  pointNew = $offset + point;
  drawAbsolute(point)
end

draw(Point.new(3, 4));

l'utilisation de $offset semble un peu bizarre.

En C, si je définis quelque chose en dehors de toute fonction, c'est automatiquement une variable globale. Pourquoi dans Ruby doit-il être $offset mais ne peut pas être offset et reste global? Si c'est offset, alors c'est un local? Mais local à où, car il se sent très global.

Existe-t-il de meilleures façons d'écrire le code ci-dessus? L'utilisation de $offset peut sembler un peu moche au début.


Mise à jour: je peux mettre ce décalage dans une définition de class, mais que se passe-t-il si deux ou plusieurs classes doivent utiliser cette constante? Dans ce cas, dois-je encore définir un $offset?

66

Une chose que vous devez réaliser est dans Ruby tout est un objet. Cela étant, si vous ne définissez pas vos méthodes dans Module ou Class, = Ruby le placera dans la classe Object. Votre code sera donc local à la portée de Object.

Une approche typique de la programmation orientée objet consiste à encapsuler toute la logique dans une classe:

class Point
  attr_accessor :x, :y

  # If we don't specify coordinates, we start at 0.
  def initialize(x = 0, y = 0)
    # Notice that `@` indicates instance variables.
    @x = x
    @y = y
  end

  # Here we override the `+' operator.
  def +(point)
    Point.new(self.x + point.x, self.y + point.y)
  end

  # Here we draw the point.
  def draw(offset = nil)
    if offset.nil?
      new_point = self
    else
      new_point = self + offset 
    end
    new_point.draw_absolute
  end

  def draw_absolute
    puts "x: #{self.x}, y: #{self.y}"
  end
end

first_point = Point.new(100, 200)
second_point = Point.new(3, 4)

second_point.draw(first_point)

J'espère que cela clarifie un peu.

53
Igor

La portée de la variable dans Ruby est contrôlée dans une certaine mesure par les sigils. Variables commençant par $ sont globaux, les variables avec @ sont des variables d'instance, @@ désigne des variables de classe et les noms commençant par une lettre majuscule sont des constantes. Toutes les autres variables sont locales. Lorsque vous ouvrez une classe ou une méthode, il s'agit d'une nouvelle portée et les locals disponibles dans la portée précédente ne sont pas disponibles.

Je préfère généralement éviter de créer des variables globales. Il y a deux techniques qui atteignent généralement le même but que je considère plus propres:

  1. Créer une constante dans un module. Donc, dans ce cas, vous placeriez toutes les classes qui ont besoin du décalage dans le module Foo et créez une constante Offset, afin que toutes les classes puissent accéder à Foo::Offset.

  2. Définissez une méthode pour accéder à la valeur. Vous pouvez définir la méthode globalement, mais encore une fois, je pense qu'il est préférable de l'encapsuler dans un module ou une classe. De cette façon, les données sont disponibles où vous en avez besoin et vous pouvez même les modifier si vous en avez besoin, mais la structure de votre programme et la propriété des données seront plus claires. Ceci est plus conforme aux principes de conception de OO).

110
Chuck

L'une des raisons pour lesquelles la variable globale a besoin d'un préfixe (appelé "sigil") est que, dans Ruby, contrairement à C, vous n'avez pas à déclarer vos variables avant de les affecter. Le sceau est utilisé comme un moyen d’être explicite sur la portée de la variable.

Sans préfixe spécifique pour les globales, étant donné une instruction pointNew = offset + point dans votre méthode draw, alors offset fait référence à une variable locale dans la méthode (et aboutit à un NameError dans ce cas). Même chose pour @ utilisé pour désigner les variables d'instance et @@ pour les variables de classe.

Dans d'autres langages qui utilisent des déclarations explicites telles que C, Java etc., l'emplacement de la déclaration sert à contrôler la portée.

10
mikej