Есть ли лучший способ сделать class_eval () для извлечения переменных класса, в Ruby? - PullRequest
2 голосов
/ 02 июня 2011

Лично я ничего не имею против этого, кроме того, что это долго, но что меня действительно беспокоит, так это слово eval.

Я делаю много вещей в JavaScript, и я работаю из чего-то, напоминающего eval, как будто это дьявол, мне также не нравится тот факт, что параметр является строкой (опять же, вероятно, потому что это eval).

Я знаю, что мог бы написать свой собственный метод, чтобы исправить проблему с именем-длины-метода , мою проблему с именем метода и параметр-будучи-строкой вещь, но я действительно хочу знать: Есть ли лучший, более короткий, изящный, но нативный способ сделать class_eval для извлечения переменных класса?

Примечание: я знаю о существовании class_variable_get() и class_variables(), но на самом деле они мне не нравятся;ужасно долго, не так ли?

РЕДАКТИРОВАТЬ: Обновлено вопрос, чтобы быть более конкретным.

Спасибо!

1 Ответ

8 голосов
/ 02 июня 2011

Используйте class_variable_get, но только если вы должны

class_variable_get является лучшим способом, за исключением того факта, что он не «привлекательный» для вас. Если вы входите в класс и нарушаете инкапсуляцию, возможно, уместно иметь этот дополнительный барьер, чтобы указать, что вы делаете что-то не так.

Создание методов доступа для переменных, к которым вы хотите получить доступ

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

class Foo
  def self.bar
    @@bar
  end
end
p Foo.bar

Однако, если это ваш класс, вы уверены, что вам нужны переменные класса? Если вы не понимаете последствий (см. Ниже), возможно, вам действительно нужны переменные экземпляра самого класса:

class Foo
  class << self
    attr_accessor :bar
  end
end

Foo.bar = 42
p Foo.bar

Поведение переменных класса

Переменные класса кажутся новичкам как правильный способ хранения информации на уровне класса, в основном из-за названия. Они также удобны, потому что вы можете использовать один и тот же синтаксис для чтения и записи, независимо от того, используете ли вы метод класса или метод экземпляра. Однако переменные класса являются общими для класса и всех его подклассов.

Например, рассмотрим следующий код:

class Rectangle
  def self.instances
    @@instances ||= []
  end
  def initialize
    (@@instances ||= []) << self
  end
end

class Square < Rectangle
  def initialize
    super
  end
end

2.times{ Rectangle.new }
p Rectangle.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>]

Square.new
p Square.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>, #<Square:0x25c76d0>]

Ack! Прямоугольники не квадраты! Вот лучший способ сделать то же самое:

class Rectangle
  def self.instances
    @instances ||= []
  end
  def initialize
    self.class.instances << self
  end
end

class Square < Rectangle
  def initialize
    super
  end
end

2.times{ Rectangle.new }
p Rectangle.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>]

2.times{ Square.new }
p Square.instances
#=> [#<Square:0x25c76d0>, #<Square:0x25c76b8>]

Путем создания переменной экземпляра и методов accesor для самого класса, который является экземпляром класса Class, аналогично MyClass = Class.new - всем экземплярам класса (и посторонним ) иметь общее чистое место для чтения / записи информации, которая не используется другими классами.

Обратите внимание, что явное отслеживание каждого созданного экземпляра предотвратит сборку мусора в «неиспользуемых» экземплярах. Осторожно используйте код, подобный приведенному выше.

Использование class_eval более чистым способом

Наконец, если вы собираетесь использовать class_eval, обратите внимание, что он также имеет блочную форму, которая не должна анализировать и лексировать строку для ее оценки:

Foo.class_eval('@@bar') # ugh
Foo.class_eval{ @@bar } # yum
...