Я хотел бы, чтобы усилить то, что другие сказали, и, в частности, сравнить использование переменных класса с экземпляра класса переменных. Давайте начнем с этого:
class Holder1
end
class Holder2 < Holder1
@@var = 99
# Create a class setter and a getter for the class variable
def Holder2.var=(val)
@@var = val
end
def Holder2.var
@@var
end
# Create a instance getter for the class variable
def var
@@var
end
end
class Holder3 < Holder2
end
Holder2.var #=> 99
Holder2.var = 1
Holder3.var #=> 1
Holder1.var #=> NoMethodError: undefined method `var' for Holder1:Class
Holder2.new.var #=> 1
Holder3.new.var #=> 1
Holder3.var = 2
Holder3.var #=> 2
Holder2.var #=> 2
Holder3.new.var #=> 2
Holder2.new.var #=> 2
Помимо: первые два метода, как правило, записывается следующим образом:
def self.var=(val)
@@var = val
end
def self.var
@@var
end
Это работает, потому что self
=>Holder2
когда методы создания. Не устанавливая имя класса, никаких изменений не требуется, если вы решите переименовать класс.
Теперь это (использование переменной класса) может быть именно тем поведением, которое вы хотите.То есть, если вы хотите, чтобы подклассы видели переменную и могли ее изменить, переменная класса - это то, что вам нужно.
Если, однако, вы хотите, чтобы каждый подкласс иметь свою собственную переменную, которая не может быть ни видно, ни изменен подклассов, вы должны использовать экземпляр класса переменной, @var
, а не переменную класса, @@var
. (Технически это не совсем правильно, потому что можно было использовать Holder2.instance_variable_get(:@var)
или Holder2.instance_variable_set(:@var)
в любой точке вашей программы.)
Сравните результаты кода, приведенного выше. Я включил переменную экземпляра с тем же именем, что и переменная экземпляра класса, @var
, чтобы показать, что они такие же разные, как и @night
и @day
.
class Holder1
end
class Holder2 < Holder1
# Create an accessor for the instance variable
attr_accessor :var
# Initialize class instance variable
@var = 99
# Create an accessor for the class instance variable
class << self
puts "self in 'class << self': #{self}"
attr_accessor :var
def our_dog
"Diva"
end
end
# Create a class method
def self.our_cat
puts "self in 'self.our_cat())' def: #{self}"
"Teagan"
end
# Create an instance setter and a getter for the class instance variable
def c_ivar=(val)
self.class.var = val
end
def c_ivar
self.class.var
end
end
class Holder3 < Holder2
end
#=> self in 'class << self': #<Class:Holder2>
Holder2.var #=> 99
h2 = Holder2.new
h2.var #=> nil
Holder2.var = 1
Holder2.var #=> 1
h2.c_ivar #=> 1
h2.c_ivar = 2
Holder2.var #=> 2
h2.var #=> nil
h2.var = 3
h2.var #=> 3
Holder2.var #=> 2
Holder3.var #=> nil
Holder1.var #=> NoMethodError: undefined method `var' for Holder1:Class
Holder3.var = 4
Holder3.var #=> 4
Holder2.var #=> 2
h3 = Holder3.new
h3.c_ivar #=> 4
h2.c_ivar #=> 2
Holder3.our_dog #=> "Diva"
Holder3.our_cat #=> "self in 'self.our_cat())' def: Holder3"
#=> "Teagan"
Holder1.var
вызывает исключение, потому что класс не имеет доступа к экземпляру класса Holder2
«s переменной [email protected]
. Напротив, первое использование Holder3.var
возвращает nil
, потому что переменная существует для Holder3
по наследству, но не была инициализирована.
Выражение class << self
не меняет self
от Holder2
для одноплодного класса Holder2
«s (ака метакласса) до следующего end
. Обратите внимание, что Ruby обозначает одноэлементный класс Holder2
как #<Class:Holder2>
. Кроме того, нам не нужно добавлять my_dog
с self.
, так как self
- это одноэлементный класс Holder2
при создании метода.
Обратите внимание, что:
Holder2.singleton_methods #=> [:var, :var=, :our_dog, :our_cat]
Это показывает, что our_cat()
является методом Holder2
«s одноплодного класса, несмотря на то, self
был Holder2
(а не Holder2
's одноэлементного класса', #<Class:Holder2>
), когда метод был построен , По этой причине некоторые говорят, что технически "there is no such thing as a class method". Это также говорит нам о том, что мы могли бы переместить определение my_cat
в конструкцию class << self
(и упали self.
).
Другой распространенный способ добавить переменные экземпляра и методы Holder2
«одноплодной класса s является замена class << self ... end
с extend M
и создать модуль:
module M
attr_accessor :var
def our_dog
"Diva"
end
end
Object#extend смешивает эти переменные экземпляра и методы в Holder2
» s одноплодной класса ,