簡體   English   中英

在Ruby中初始化類實例變量

[英]Initializing class instance variables in Ruby

我正在開發一個小型應用程序,並且遇到了ruby的OOP模型的問題。 我有以下簡化的類結構。

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

我現在的問題是,當我在Baz的類定義中調用add_bar時, @bar顯然沒有初始化,並且我得到一個錯誤, +運算符不可用於nil 直接在Foo上調用add_bar不會產生此問題。 為什么這樣,我如何正確初始化@bar

為了弄清楚我想要什么,我將指出我期望從這些類中獲得的行為。

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

我怎么能實現這個目標?

簡短回答:實例變量不會被子類繼承

更長的答案:問題是你在類的主體中寫了@bar = [] (在任何方法之外)。 設置實例變量時,它存儲在當前self任何內容中。 當你在一個類體中時, self就是類對象Foo。 因此,在您的示例中, @foo foo在類對象Foo上定義。

稍后,當您嘗試查找實例變量時,Ruby會查找當前self 當你從Baz調用add_bar時, self就是Baz。 在add_bar的主體中, self也是STILL Baz(即使該方法在Foo中)。 所以,Ruby在Baz中查找@bar並且找不到它(因為你在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 (又名“The Pickaxe”)中的“Metaprogramming”章節時,這個概念終於點擊了我。

我如何解決你的問題:看看Rails的class_attribute方法。 它允許你嘗試做的事情(在父類中定義一個可以在其子類中繼承(和覆蓋)的屬性)。

好吧,因為@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

在這里閱讀更多相關信息。

我是這樣做的:

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

這似乎運作良好:

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"]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM