Как вы используете глобальные переменные или постоянные значения в Ruby?

У меня есть программа, которая выглядит так:

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

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

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

использование $offset кажется немного странным.

В C, если я определяю что-то вне любой функции, это глобальная переменная автоматически. Почему в Ruby он должен быть $offset, но не может быть offset и по-прежнему глобальным? Если это offset, то это локально? Но локально, где, потому что он чувствует себя очень глобально.

Есть ли лучшие способы написания кода выше? Использование $offset может показаться немного уродливым вначале.


Обновление: я могу поместить это смещение внутри определения class, но что, если два или несколько классов должны использовать эту константу? В этом случае мне еще нужно определить $offset?

Ответ 1

Одна вещь, которую вам нужно понять, в Ruby - это объект. Учитывая это, если вы не определяете свои методы в пределах Module или Class, Ruby поместит его в класс Object. Таким образом, ваш код будет локальным для области Object.

Типичный подход к объектно-ориентированному программированию заключается в инкапсуляции всей логики внутри класса:

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)

Надеюсь, это немного разъяснит.

Ответ 2

Переменная область в Ruby в некоторой степени контролируется сигилами. Переменные, начинающиеся с $, являются глобальными, переменные с @ являются переменными экземпляра, @@ означает переменные класса, а имена, начинающиеся с заглавной буквы, являются константами. Все остальные переменные являются локальными. Когда вы открываете класс или метод, новая область и локали, доступные в предыдущей области, недоступны.

Обычно я предпочитаю избегать создания глобальных переменных. Есть два метода, которые обычно достигают той же цели, что я считаю более чистым:

  • Создайте константу в модуле. Поэтому в этом случае вы должны поместить все классы, которым требуется смещение в модуле Foo, и создать константу Offset, чтобы все классы могли получить доступ к Foo::Offset.

  • Определите метод доступа к значению. Вы можете определить метод глобально, но опять же, я думаю, что лучше инкапсулировать его в модуле или классе. Таким образом, данные доступны там, где они вам нужны, и вы даже можете изменить их, если вам нужно, но структура вашей программы и права собственности на данные будут более четкими. Это больше соответствует принципам проектирования OO.

Ответ 3

Одна из причин, по которой глобальной переменной нужен префикс (так называемый "сигил"), заключается в том, что в Ruby, в отличие от C, вам не нужно объявлять переменные перед их присвоением. Сигил используется как способ явно указать область действия переменной.

Без специального префикса для глобалов, учитывая оператор pointNew = offset + point внутри вашего метода draw, тогда offset ссылается на локальную переменную внутри метода (и в этом случае приводит к NameError). То же самое для @ используется для ссылки на переменные экземпляра и @@ для переменных класса.

В других языках, которые используют явные объявления, такие как C, Java и т.д., Размещение объявления используется для управления областью действия.

Ответ 4

Я думаю, что это локально для файла, который вы заявили о смещении. Рассмотрите каждый файл как сам метод.

Возможно, поместите все это в класс, а затем сделайте смещение переменной класса с помощью @@offset = Point.new(100, 200);?