Обо мне

Моя фотография
Дикий Полярный Сов

среда, 13 января 2010 г.

Class instance variables в Ruby

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

А все дело в чем? В Руби каждый класс - это тоже объект, а переменные типа @@variable, которые я раньше считал "переменными класса", на самом деле принадлежат иерархии наследования (как-то так).
То есть, сочинив классу потомка, мы будем иметь доступ не к новой @@variable, а все к той же самой, и, поменяв ее в потомке, мы изменим ее и в предке! Не совсем то, что надо, если речь идет о переменной класса (ЕМНИП, в  C++ оно называлось static).

Дальше начинается магия. В Руби _все_ является объектом, в том числе, как уже было сказано, и сам класс. А значит, мы можем использовать @variable в определении класса, и эта самая @variable будет принадлежать не иерархии наследования, а только самому нашему классу.

Но как организовать к этой переменной доступ? Об этом - после рекламы далее.




Для этого мы будем использовать Eigenclass (собственный класс). До конца я в этом еще не разобрался, но по сути, у каждого класса Руби есть этакий надкласс (Singleton class, Eigenclass, как угодно), объектом которого и является этот самый каждый класс. То есть переменные класса (те, что @variable, но не переменные экземпляра) - всего лишь instance variables of Eigenclass.

Получаем такую запись:


class A
  @x = 1 #вот наша переменная, которая не должна 
         #отойти к наследникам
  class << self #а вот тут происходит магия - мы 
                #обращаемся к Eigenclass!
    attr_accessor :x #для которого x - не более чем  
                     #instance variable, к которой он 
                     #может обращаться, как и к любой i_v
  end
end
#Теперь вполне можно использовать
A.x # => 1
class B < A
  @x = 5
end
A.x # => 1
B.x # => 5

То есть, когда мы унаследуем от класса А, мы получаем другую переменную @x (используя @@x и не используя эти телодвижения, мы бы получили переменную уровня иерархии наследования, которая поменялась бы в предке, если бы мы поменяли ее в наследнике).

Как-то так.

Update: Проверил в irb - вроде как можно выкрутиться еще проще, без явного привлечения магии Eigenclass:


class A
  @x = 1
  def self.x
    @x
  end
end

Не получится "красиво сделать аксессор", но зато работать все должно так же. И наследники подгадить не смогут.

Комментариев нет:

Отправить комментарий