Должен ли я использовать переменные класса или переменные экземпляра класса для статических переменных класса в Ruby?
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
Я хочу знать, какой из них мне следует использовать? Каковы преимущества и недостатки каждого из них?
Переменные класса:
- закрытый, если вы не создадите интерфейс
- делится между наследниками
- короче писать
Переменные экземпляра класса:
- Public, так как для доступа к ним необходимо использовать интерфейс
- не разделяются между наследствами, но при наследовании равны нулю
- дольше, чтобы напишите
Что еще я должен иметь в виду?
4 ответа:
Недавно я обнаружил 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
Подробнее здесь
Просто для полноты: из двух представленных вариантов я предпочитаю идти с переменными класса-экземпляра, так как часто это ожидаемое поведение.
Никогда не используйте переменные класса в Ruby, никогда. Они вызывают проблемы с наследованием. Вместо этого всегда используйте переменные экземпляра класса.
Вы также можете объявить переменные экземпляра на уровне класса:
В целом, я бы избегал переменных класса, поскольку модель совместного наследования часто очень удивительна и неочевидна; честно говоря, я не уверен, какую полезность они действительно предлагают, кроме того, что они не совсем глобальны.class Foo @bar = 'baz' def Foo.print puts @bar end end
Если вам нужна глобальная переменная 'scoped', я бы выбрал переменные экземпляра уровня класса с аксессорами. Вы избегаете наследования "сюрприз", сохраняя при этом инкапсуляция.
Переменные экземпляра класса, как правило, более полезны и менее удивительны, чем переменные класса, поэтому их, вероятно, следует использовать.
А теперь я изложу некоторые вещи в мучительных деталях, которые вы ожидаете от ответа Ruby на StackOverflow:
Чтобы объявить или сослаться на переменную экземпляра класса, необходимо находиться в области класса и использовать одиночный знак @ . Это помещает переменную в экземпляр синглетного класса Для этого класс.
К сожалению, когда вы находитесь в области видимости класса и используете ключевое слово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
и тому подобное, но это должно удовлетворить вас на данный момент. :- ))Последнее замечание: переменные экземпляра класса доступны только через класс, в котором они определены. Если вы попытаетесь позвонить кому-либо из них эти методы из (или через) подкласса, методы будут выполняться, но все переменные @ будут равны нулю, так как
self
будет объект класса подкласса, а не объект класса родительского класса.class Bar < Foo end p [Bar.a, Bar.b, Bar.c, Bar.d] #=> [nil, nil, nil, nil]