Class-level instance variables

в 8:14, , рубрики: junior developer, ruby, variables, метки: , ,

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

class User
  @var = 1
end

Ожидаемое поведение

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

class A
  def set_a( v );  @a = v;  end
  def get_a; return @a; end
end

o1 = A.new
o1.instance_variables   # => []
o1.get_a   # => nil

o1.set_a 123
o1.instance_variables   # => ["@a"]
o1.get_a   # => 123

o2 = A.new
o2.get_a   # => nil

Переменная появляется в экземпляре после ее установки, дальше все работает как по маслу — она видна только в своем экземпляре.

Значение по-умолчанию

Неожиданности с переменныой экземпляра обычно начинаются когда нужно установить значение ее по-умолчанию:

class B
  attr_accessor :b       # вместо set_b, get_b из вышестоящего примера
  @b = 1
end

o1 = B.new
o1.b    # => nil
o1.instance_variables   # => []

o1.b = 2
o1.b    # => 2
o1.instance_variables   # => ["@b"]

Оп-па. А где же наше значение по-умолчанию? Почему переменной вообще нет в обьекте с самого начала?

Такие переменные называются Class-Level Instance Variable (как это по-русски «переменные экземпляра Class-уровня»?)

Классы это объекты.

На Ruby, что любой код выполняется в контексте текущего обьекта (экземпляра класса), текущий объект всегда доступен из псевдо-переменной self. Это базовые истинины. Но попорбуйте ответить на вопрос, в каком конексте (объекте) выполняется строка 4 и строка 2?

1: class User
2:   @b = 1
3:   def a
4:     @a = 1
5:   end
6: end

Строка 4 выполнится в экземпляре класса User, как мы и ожидаем, в объекте который вы создадите через User.new

А строка 2 выполняется в экземпляре класса Class, в объекте который описывает класс User. Этот объект создатся сразу после чтения интерпретатором класса User.
Само слово User это не что иное как консанта, значение которой объект типа Class. Этот объект описывает наш класс User. Именно к этому объекту относится переменная @b.

o1 = B.new
o1.b    # => nil
o1.instance_variables   # => []

B.instance_variables    # => ["@b"]

На самом деле она всегда была там. Не в классе B и не в экземпляре класса B, а в экземпляре класса Class хранимом в константе B.

Применение

Что нам дают переменные экземпляра Class-уровня?

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

class B
  @b = 1
  def self.set_b( v ); @b = v;  end
  def self.get_b; @b; end
end

B.get_b   # => 1

B.set_b 2
B.get_b   # => 2

class B1 < B; end

B1.get_b  # => 1
B1.set_b 3
B1.get_b   # => 3

B.get_b    # => 2

То есть при каждом наследовании от класса, в котором была объявлена такая переменная будет создаваться новая независимая. Сравните с поведением переменной класса:

class C
  @@c = 1
  def self.set_c( v ); @@c = v;  end
  def self.get_c; @@c; end
end

C.get_c   # => 1

C.set_b 2
C.get_b   # => 2

class C1 < C; end
C1.get_c  # => 2

C1.set_c 3
C1.get_c   # => 3
C.get_c    # => 3

class C2 < C; @@c = 4; end
C.get_c   # => 4
C1.get_c   # => 4
C2.get_c   # => 4

Accessors

Такие переменные с помощью акцессоров создаются следующим образом:

class A
  class << self
    attr_accessor :a
  end
  # Вместо этого такая строка: self.class.attr_accessor :a
  # невозможна только потому, что attr_accessor это private method :(
end

A.instance_variables # => ["@a"]
A.class_variables    # => []
A.a   # => nil

A.a = 123
A.a   # => 123

Обратите внимание что рельсовый cattr_accessor :a создаст методы с такими же именами, только обращаться они будут к переменной класса.

class User
  cattr_accessor :a
end

User.instance_variables  # => []
User.class_variables     # => ["@@a"]

Материалы по теме:

railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
wiseheartdesign.com/articles/2006/09/22/class-level-instance-variables/
snippets.dzone.com/posts/show/4382

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

Автор: dapi

Источник

Поделиться

* - обязательные к заполнению поля