Ruby 类实例变量和继承

发布于 2024-08-31 04:45:47 字数 1430 浏览 7 评论 0原文

我有一个名为 LibraryItem 的 Ruby 类。我想将这个类的每个实例与属性数组相关联。这个数组很长,看起来像这样:

['title', 'authors', 'location', ...]

注意,这些属性实际上并不是方法,只是 LibraryItem 具有的属性列表。

接下来,我想创建一个名为 LibraryBookLibraryItem 子类,它具有一个属性数组,其中包含 LibraryItem 的所有属性,但也包含还有更多。

最终,我需要 LibraryItem 的几个子类,每个子类都有自己的数组 @attributes 版本,但每个子类都添加到 LibraryItem@attributes(例如,LibraryBookLibraryDVDLibraryMap 等)。

所以,这是我的尝试:

class LibraryItem < Object
  class << self; attr_accessor :attributes; end
  @attributes = ['title', 'authors', 'location',]
end

class LibraryBook < LibraryItem
  @attributes.push('ISBN', 'pages')
end

这不起作用。我收到错误

undefined method `push' for nil:NilClass

如果它能工作,我希望

puts LibraryItem.attributes 
puts LibraryBook.attributes

输出

['title', 'authors', 'location']
['title', 'authors', 'location', 'ISBN', 'pages']

类似的内容(添加于 2010 年 5 月 2 日) 解决此问题的一种方法是使 @attributes 成为一个简单的实例变量,然后在 initialize 方法中添加 LibraryBoot 的新属性(这是建议的)由德马斯在其中一个答案中提出)。

虽然这肯定会起作用(事实上,这也是我一直在做的事情),但我对此并不满意,因为它不是最佳的:为什么每次创建对象时都要构造这些不变的数组?

我真正想要的是拥有可以从父类继承的类变量,但在子类中更改时不会在父类中更改。

I have a Ruby class called LibraryItem. I want to associate with every instance of this class an array of attributes. This array is long and looks something like

['title', 'authors', 'location', ...]

Note that these attributes are not really supposed to be methods, just a list of attributes that a LibraryItem has.

Next, I want to make a subclass of LibraryItem called LibraryBook that has an array of attributes that includes all the attributes of LibraryItem but will also include many more.

Eventually I will want several subclasses of LibraryItem each with their own version of the array @attributes but each adding on to LibraryItem's @attributes (e.g., LibraryBook, LibraryDVD, LibraryMap, etc.).

So, here is my attempt:

class LibraryItem < Object
  class << self; attr_accessor :attributes; end
  @attributes = ['title', 'authors', 'location',]
end

class LibraryBook < LibraryItem
  @attributes.push('ISBN', 'pages')
end

This does not work. I get the error

undefined method `push' for nil:NilClass

If it were to work, I would want something like this

puts LibraryItem.attributes 
puts LibraryBook.attributes

to output

['title', 'authors', 'location']
['title', 'authors', 'location', 'ISBN', 'pages']

(Added 02-May-2010)
One solution to this is to make @attributes a simple instance variable and then add the new attributes for LibraryBoot in the initialize method (this was suggested by demas in one of the answers).

While this would certainly work (and is, in fact, what I have been doing all along), I am not happy with this as it is sub-optimal: why should these unchanging arrays be constructed every time an object is created?

What I really want is to have class variables that can inherit from a parent class but when changed in the child class do not change in the the parent class.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

忘年祭陌 2024-09-07 04:45:47

另一种解决方案是使用 inherited 钩子:

class LibraryItem < Object
  class << self
    attr_accessor :attributes
    def inherit_attributes(attrs)
      @attributes ||= []
      @attributes.concat attrs
    end

    def inherited(sublass)
      sublass.inherit_attributes(@attributes)
    end
  end
  @attributes = ['title', 'authors', 'location',]
end

class LibraryBook < LibraryItem
  @attributes.push('ISBN', 'pages')
end

Another solution would be to use the inherited hook:

class LibraryItem < Object
  class << self
    attr_accessor :attributes
    def inherit_attributes(attrs)
      @attributes ||= []
      @attributes.concat attrs
    end

    def inherited(sublass)
      sublass.inherit_attributes(@attributes)
    end
  end
  @attributes = ['title', 'authors', 'location',]
end

class LibraryBook < LibraryItem
  @attributes.push('ISBN', 'pages')
end
顾忌 2024-09-07 04:45:47

既然您提到属性是“固定的”和“不变的”,我假设您的意思是一旦创建对象就永远不会更改它们的值。在这种情况下,类似下面的内容应该可以工作:

class Foo
    ATTRS = ['title', 'authors', 'location']
    def attributes
        ATTRS
    end
end

class Bar < Foo
    ATTRS = ['ISBN', 'pages']
    def attributes
        super + ATTRS
    end
end

您手动实现一个读取器方法(而不是让 attr_accessor 为您创建它)来伪装数组的内部名称。在您的子类中,您只需调用祖先类的 reader 函数,添加与子类关联的其他字段,然后将其返回给调用者。对于用户来说,这看起来就像一个名为 attributes 的只读成员变量,它在子类中具有其他值。

Since you mention that the attributes are "fixed" and "unchanging", I am assuming that you mean that you will never change their value once the object is created. In that case, something like the following should work:

class Foo
    ATTRS = ['title', 'authors', 'location']
    def attributes
        ATTRS
    end
end

class Bar < Foo
    ATTRS = ['ISBN', 'pages']
    def attributes
        super + ATTRS
    end
end

You are manually implementing a reader method (instead of letting attr_accessor create it for you) that disguises the internal name of the array. In your subclass, you simply call the ancestor class' reader function, tack on the additional fields associated with the child class, and return that to the caller. To the user, this appears like a read-only member variable named attributes that has additional values in the sub-class.

断念 2024-09-07 04:45:47

就像一个版本:

class LibraryItem < Object
  def initialize
    @attributes = ['one', 'two'];
  end
end

class LibraryBook < LibraryItem
  def initialize
   super
   @attributes.push('three')
 end
end

b = LibraryBook.new

Just as a version:

class LibraryItem < Object
  def initialize
    @attributes = ['one', 'two'];
  end
end

class LibraryBook < LibraryItem
  def initialize
   super
   @attributes.push('three')
 end
end

b = LibraryBook.new
心头的小情儿 2024-09-07 04:45:47

出于好奇,这样的事情会起作用吗?

class Foo
  ATTRIBUTES = ['title','authors','location']
end

class Bar < Foo
  ATTRIBUTES |= ['ISBN', 'pages']
end

这似乎会产生所需的结果 - 创建类对象时 ATTRIBUTES 数组会扩展,并且 ATTRIBUTES 的值会按预期变化:

> Foo::ATTRIBUTES
=> ['title','authors','location'] 
> Bar::ATTRIBUTES
=> ['title','authors','location', 'ISBN', 'pages'] 

Out of curiosity, will something like this work?

class Foo
  ATTRIBUTES = ['title','authors','location']
end

class Bar < Foo
  ATTRIBUTES |= ['ISBN', 'pages']
end

This would seem to produce the desired result - the ATTRIBUTES array is expanded when the class object is created, and the values of ATTRIBUTES varies as expected:

> Foo::ATTRIBUTES
=> ['title','authors','location'] 
> Bar::ATTRIBUTES
=> ['title','authors','location', 'ISBN', 'pages'] 
廻憶裏菂餘溫 2024-09-07 04:45:47

为了扩展@Nick Vanderbilt 的答案,使用 active_support 来执行此操作,这正是我想要的此功能的简写。这是一个完整的例子:

require 'active_support/core_ext'

class Foo
  class_attribute :attributes
  self.attributes = ['title','authors','location']
end

class Bar < Foo
  self.attributes = Foo.attributes + ['ISBN', 'pages']
end

puts Foo.attributes.inspect #=> ["title", "authors", "location"]
puts Bar.attributes.inspect #=> ["title", "authors", "location", "ISBN", "pages"]

遗憾的是,红宝石在不需要库的情况下很难实现这一目标。这是我唯一怀念 python 的东西。就我而言,我不介意对 active_support gem 的依赖。

To expand on @Nick Vanderbilt's answer, using active_support you do this, which is exactly the short hand I want for this functionality. Here's a complete example:

require 'active_support/core_ext'

class Foo
  class_attribute :attributes
  self.attributes = ['title','authors','location']
end

class Bar < Foo
  self.attributes = Foo.attributes + ['ISBN', 'pages']
end

puts Foo.attributes.inspect #=> ["title", "authors", "location"]
puts Bar.attributes.inspect #=> ["title", "authors", "location", "ISBN", "pages"]

Shame it's so difficult for ruby to achieve this without needing a library for it. It's the only thing I miss from python. And in my case, I don't mind the dependency on the active_support gem.

不奢求什么 2024-09-07 04:45:47

ActiveSupport 在 Rails Edge 中有 class_attribute 方法。

ActiveSupport has class_attribute method in rails edge.

乞讨 2024-09-07 04:45:47

在 LibraryBook 中,变量 @attributes 是一个新的自变量,是对象 LibraryBook 的实例变量,因此它未初始化,并且您会收到错误“未定义的方法... for nil”
在使用之前,您应该通过 LibraryItem 属性列表对其进行初始化

class LibraryBook < LibraryItem
  @attributes = LibraryItem::attributes + ['ISBN', 'pages']
end

In LibraryBook variable @attributes is a new independent variable, instance variable of object LibraryBook, so its not initialized and you get error "undefined method ... for nil"
You should to initialize it by LibraryItem attribut's list before using

class LibraryBook < LibraryItem
  @attributes = LibraryItem::attributes + ['ISBN', 'pages']
end
挽手叙旧 2024-09-07 04:45:47

这是针对字符串(实际上是任何东西),而不是数组,但是......

class A
  def self.a
    @a || superclass.a rescue nil
  end

  def self.a=(value)
    @a = value
  end

  self.a = %w( apple banana chimp )
end

class B < A
end

class C < B
  self.a += %w( dromedary elephant )
end

class D < A
  self.a = %w( pi e golden_ratio )
end



irb(main):001:0> require 'test2'
=> true
irb(main):002:0> A.a
=> ["apple", "banana", "chimp"]
irb(main):003:0> B.a
=> ["apple", "banana", "chimp"]
irb(main):004:0> C.a
=> ["apple", "banana", "chimp", "dromedary", "elephant"]
irb(main):005:0> D.a
=> ["pi", "e", "golden_ratio"]
irb(main):006:0> A.a = %w( 7 ) 
=> ["7"]
irb(main):007:0> A.a
=> ["7"]
irb(main):008:0> B.a
=> ["7"]
irb(main):009:0> C.a = nil
=> nil
irb(main):010:0> C.a
=> ["7"]

This is for strings (anything really), rather than arrays, but...

class A
  def self.a
    @a || superclass.a rescue nil
  end

  def self.a=(value)
    @a = value
  end

  self.a = %w( apple banana chimp )
end

class B < A
end

class C < B
  self.a += %w( dromedary elephant )
end

class D < A
  self.a = %w( pi e golden_ratio )
end



irb(main):001:0> require 'test2'
=> true
irb(main):002:0> A.a
=> ["apple", "banana", "chimp"]
irb(main):003:0> B.a
=> ["apple", "banana", "chimp"]
irb(main):004:0> C.a
=> ["apple", "banana", "chimp", "dromedary", "elephant"]
irb(main):005:0> D.a
=> ["pi", "e", "golden_ratio"]
irb(main):006:0> A.a = %w( 7 ) 
=> ["7"]
irb(main):007:0> A.a
=> ["7"]
irb(main):008:0> B.a
=> ["7"]
irb(main):009:0> C.a = nil
=> nil
irb(main):010:0> C.a
=> ["7"]
永言不败 2024-09-07 04:45:47

您也可以使用常量来做到这一点。虽然没有检查。

class LibraryItem < Object
  class << self; attr_accessor :attributes; end
  ATTRIBUTES = ['title', 'authors', 'location',]
end

class LibraryBook < LibraryItem
  ATTRIBUTES .push('ISBN', 'pages']
end

You can do it using CONSTANTS also. No check though.

class LibraryItem < Object
  class << self; attr_accessor :attributes; end
  ATTRIBUTES = ['title', 'authors', 'location',]
end

class LibraryBook < LibraryItem
  ATTRIBUTES .push('ISBN', 'pages']
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文