2016-09-30 48 views
2

我與attr_accessor一類這樣設置:繼承attr_accessor內恆定

class Human 
    ATTRIBUTES = [:name] 
    attr_accessor *ATTRIBUTES 
end 

它就像一個魅力,讓我保持內部屬性屬性不變。問題是我希望有一個類Student繼承自Human類,而不需要每次都放置attr_accessor。 基本上我想有是這樣的:

class Student < Human 
    ATTRIBUTES = [:name, :school] 
end 

不幸的是,當我做

Student.new.school 

我沒有得到任何方法錯誤,因爲attr_accessor從人,而不是一個學生裝。我應該用什麼構造來實現我的目標?

+0

這樣的學生類沒有attr_accessor,所以它不知道有學校屬性。如果你想讓學校認可,你應該在Student類中擁有attr_accessor,或者在Human類中聲明學校attib。 – uday

+1

你甚至知道'attr_accessor'是什麼嗎?從我能理解你的問題,你不知道。這似乎是潛在的問題。 –

回答

2

那麼,雖然我不需要保持數組中的屬性,但Student類將繼承其父類中定義的attr_accessor

例如:

class Human 
    attr_accessor :name, :gender 
end 

class Student < Human 
    attr_accessor :school 
end 

學生類現在有:姓名,性別和:學校attr_accessor的:

> Student.new.respond_to?(:name) 
=> true 
> Student.new.respond_to?(:name=) 
=> true 
> Student.new.respond_to?(:school) 
=> true 
> Student.new.respond_to?(:school=) 
=> true 

人也響應:name:gender

> Human.new.respond_to?(:name) 
=> true 
> Human.new.respond_to?(:gender) 
=> true 

但不去學校

> Human.new.respond_to?(:school) 
=> false 

它更清潔,它是紅寶石的方式,更容易理解。

3

我個人同意@ lcguida的回答,但是如果你堅持遵循你提出的模式,我想出了一個小實驗。其他答案已經涵蓋了爲什麼你的解決方案不起作用,所以我沒有在這裏進入。

首先想到的是在父類的self.inherited回調上調用attr_accessor,但不幸的是,孩子的主體直到後來才加載。即便如此,有意願的地方也有辦法。如果您使用的是Ruby 2.0或更高版本,則以下實現將起作用。

module LazyAttrAccessorizer 
    def self.extended(obj) 
    TracePoint.trace(:end) do |t| 
     if obj == t.self 
     obj.send :attr_accessor, *obj::ATTRIBUTES 
     t.disable 
     end 
    end 
    end 
end 

class Human 
    extend LazyAttrAccessorizer 
    ATTRIBUTES = [:name] 
    def self.inherited(subclass) 
    subclass.extend LazyAttrAccessorizer 
    end 
end 

class Student < Human 
    ATTRIBUTES = [:name, :school] 
    # ATTRIBUTES = [:school] would also work as expected, but I think you'd like to be literal there. 
end 

> Student.new.respond_to?(:name) 
=> true 
> Student.new.respond_to?(:school) 
=> true 
+0

不錯的一個!肯定+1! –