Должен ли я использовать переменные класса или переменные экземпляра класса для статических переменных класса в 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

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

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

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

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

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

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

4 2

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]