Должен ли я использовать переменные класса или переменные экземпляра класса для статических переменных класса в Ruby? - PullRequest
2 голосов
/ 16 августа 2011
class Something
    @@variable = 'Class variable'

    def give_me
        @@variable
    end
end

class OtherThing
    @variable = 'Instance variable with an interface'

    class << self
        attr_accessor :variable
    end

    def give_me
        self.class.variable
    end
end

p Something.new.give_me
p OtherThing.new.give_me

Что я хочу знать, какой из них я должен использовать?Каковы преимущества и недостатки каждого?

Переменные класса:

  1. Приватные, если вы не сделаете интерфейс
  2. Совместное использование между наследованиями
  3. Корочедля записи

Переменные экземпляра класса:

  1. Public, поскольку для доступа к ним необходимо использовать интерфейс
  2. Не разделяются между наследованиями, ноустановить на ноль при наследовании
  3. Дольше писать

Что еще я должен иметь в виду?

Ответы [ 4 ]

3 голосов
/ 16 августа 2011

Я недавно обнаружил, что ActiveSupport определяет class_inheritable_accessor, что делает то, что делают переменные экземпляра класса с тем преимуществом, что объекты не передаются по наследству, и вы можете иметь значение по умолчанию для переменной при создании подклассов .

class Foo
  class_inheritable_accessor :x, :y
end

Foo.x = 1

class Bar < Foo
end

Bar.x #=> 1
Bar.x = 3
Bar.x #=> 3
Foo.x #=> 1

Подробнее здесь

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

3 голосов
/ 16 августа 2011

Никогда не используйте переменные класса в Ruby, никогда.Они вызывают проблемы с наследованием.Вместо этого всегда используйте переменные экземпляра класса.

1 голос
/ 28 сентября 2011

Переменные экземпляра класса, как правило, более полезны и менее удивительны, чем переменные класса, поэтому вы, вероятно, должны их использовать.

А теперь я расскажу некоторые вещи в мучительной детали, которую вы ожидаете отРубиновый ответ для StackOverflow:

Чтобы объявить или обратиться к переменной экземпляра класса, вам нужно быть в области действия класса и использовать одиночный @ знак .Это помещает переменную в экземпляр класса singleton для этого класса.

К сожалению, когда вы находитесь в области видимости класса и используете ключевое слово def, ваши методы помещаются в список методы экземпляра для этого класса и выполняются в область действия экземпляра , поэтому их переменные @ -sign будут в экземпляре, в котором они находятся.

Что мы хотимвместо этого - определять методы в классе, а не в его экземплярах.На самом деле это означает, что эти методы находятся в списке методов экземпляра для одноэлементного класса объекта класса .(Фу!)

Итак, подведем итог: существует не менее четырех идиом для переключения и определения методов в классе синглтона объекта класса Foo:

class Foo
  @a, @b, @c, @d = 1, 2, 3, 4

  # 1. pass the target to def
  def Foo.a
    @a
  end

  # 2. pass the target to def, relying on the fact that self 
  # happens to be the class object right now
  def self.b
    @b
  end

  # switch from class scope to singleton class scope
  class << self

    # 3. define a plain accessor in singleton class scope
    def c
      @c
    end

    # 4. use a macro to define an accessor
    attr_reader :d
  end

end

p [Foo.a, Foo.b, Foo.c, Foo.d]
#=> [1, 2, 3, 4]

(Вероятно, есть еще полдюжины способов сделать это, если вы включите class_eval и define_method и т.п., но это должно вас удовлетворить. :-))

Последнее замечание: классПеременные экземпляра доступны только через класс, в котором они определены.Если вы попытаетесь вызвать какой-либо из этих методов из (или через) подкласса, методы будут выполнены, но все переменные @ будут иметь значение nil, поскольку self будет объектом класса подкласса, а не объектом класса родительского класса.

class Bar < Foo
end

p [Bar.a, Bar.b, Bar.c, Bar.d]
#=> [nil, nil, nil, nil]
1 голос
/ 16 августа 2011

У вас также есть возможность объявления переменных экземпляра на уровне класса:

class Foo
    @bar = 'baz'

    def Foo.print
        puts @bar
    end
end

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

Если вам нужна глобальная переменная «scoped», я бы выбрал переменные экземпляра уровня класса с аксессорами. Вы избегаете «неожиданности» наследования, сохраняя инкапсуляцию.

...