Инициализация переменных экземпляра класса в Ruby - PullRequest
18 голосов
/ 04 февраля 2011

Я работаю над небольшим приложением rails, и у меня проблема с ООП-моделью ruby.У меня есть следующая упрощенная структура класса.

class Foo
  protected
    @bar = []
    def self.add_bar(val)
      @bar += val
    end
    def self.get_bar
      @bar
    end
end

class Baz < Foo
  add_bar ["a", "b", "c"]
end

Моя проблема в том, что когда я вызываю add_bar в определении класса Baz, @bar, по-видимому, не инициализируется, и я получаю ошибку, что + Оператор недоступен для nil.Вызов add_bar на Foo напрямую не приводит к этой проблеме.Почему это так и как я могу правильно инициализировать @bar?

Чтобы прояснить, чего я хочу, я укажу поведение, которое я ожидаю от этих классов.

Foo.add_bar ["a", "b"]
Baz.add_bar ["1", "2"]
Foo.get_bar # => ["a", "b"]
Baz.get_bar # => ["a", "b", "1", "2"]

Как можноЯ достиг этого?

Ответы [ 4 ]

42 голосов
/ 04 февраля 2011

Краткий ответ: переменные экземпляра не наследуются подклассами

Более длинный ответ: проблема в том, что вы написали @bar = [] в теле класса (вне любого метода). Когда вы устанавливаете переменную экземпляра, она сохраняется на том, что в данный момент self. Когда вы находитесь в теле класса, self является объектом класса Foo. Итак, в вашем примере @foo определяется для объекта класса Foo.

Позже, когда вы пытаетесь найти переменную экземпляра, Ruby ищет то, что в данный момент self. Когда вы вызываете add_bar из Baz, self это Baz. Также self это STILL Baz в теле add_bar (хотя этот метод в Foo). Итак, Ruby ищет @bar в Baz и не может его найти (потому что вы определили его в Foo).

Вот пример, который может прояснить ситуацию

class Foo
  @bar = "I'm defined on the class object Foo. self is #{self}"

 def self.get_bar
    puts "In the class method. self is #{self}"    
    @bar
  end

  def get_bar
    puts "In the instance method. self is #{self} (can't see @bar!)"
    @bar
  end
end

>> Foo.get_bar
In the class method. self is Foo
=> "I'm defined on the class object Foo. self is Foo"

>> Foo.new.get_bar
In the instance method. self is #<Foo:0x1056eaea0> (can't see @bar!)
=> nil

Это, по общему признанию, немного сбивает с толку и является общим камнем преткновения для людей, плохо знакомых с Ruby, так что не расстраивайтесь. Эта концепция наконец-то возникла у меня, когда я прочитал главу «Метапрограммирование» в Программирование на Ruby (он же «Кирка»).

Как бы я решил вашу проблему: Посмотрите на метод Rails class_attribute. Он учитывает то, что вы пытаетесь сделать (определение атрибута родительского класса, который может наследоваться (и переопределяться) в его подклассах).

3 голосов
/ 04 февраля 2011

Ну, поскольку @bar определен как переменная экземпляра класса, он ограничен классом Foo.Проверьте это:

class Foo
    @bar = []
end

class Baz < Foo
end

Foo.instance_variables #=> [:@bar]
Baz.instance_variables #=> []

В любом случае, для этого простого примера вы можете сделать следующее:

class Foo
  protected
    def self.add_bar(val)
      @bar ||=[]
      @bar += val
    end
    def self.get_bar
      @bar
    end
end

class Baz < Foo
  add_bar ["a", "b", "c"]
end

Подробнее об этом здесь .

2 голосов
/ 30 января 2012

Я делаю это так:

class Base

    class << self

        attr_accessor :some_var


        def set_some_var(value)
            self.some_var = value
        end

    end

end


class SubClass1 < Base
  set_some_var :foo
end

class SubClass2 < Base
  set_some_var :bar
end

Тогда он должен делать то, что вы хотите.

[8] pry(main)> puts SubClass1.some_var
foo
[9] pry(main)> puts SubClass2.some_var
bar

Обратите внимание, что метод set_some_var является необязательным, вы можете сделать SubClass1.some_var = ..., есливы предпочитаете.

Если вы хотите какое-то значение по умолчанию, добавьте что-то подобное в class << self

def some_var
    @some_var || 'default value'
end
1 голос
/ 04 февраля 2011

Это, кажется, работает хорошо:

class Foo
  protected
  @@bar = {}
  def self.add_bar(val)
    @@bar[self] ||= []
    @@bar[self] += val
  end
  def self.get_bar
    (self == Foo ? [] : @@bar[Foo] || []) + (@@bar[self] || [])
  end
end
class Baz < Foo
end

Foo.add_bar ["a", "b"]
Baz.add_bar ["1", "2"]
Foo.get_bar     # => ["a", "b"]
Baz.get_bar     # => ["a", "b", "1", "2"]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...