代码之家  ›  专栏  ›  技术社区  ›  rlandster

ruby类实例变量与继承

  •  18
  • rlandster  · 技术社区  · 14 年前

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

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

    注意,这些属性实际上不应该是方法,而应该是 图书馆项目 有。

    接下来,我想把 图书馆项目 打电话 LibraryBook 它有一个属性数组,其中包含 图书馆项目 但也将包括更多。

    最后我想要几个 图书馆项目 每个都有自己的数组版本 @attributes 但每增加一个 图书馆项目 @属性 (例如, 图书馆 , LibraryDVD , LibraryMap

    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日增补) 解决这个问题的一个办法是 @属性 LibraryBoot initialize 方法(这是demas在其中一个答案中提出的)。

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

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

    9 回复  |  直到 12 年前
        1
  •  9
  •   bta    14 年前

    既然您提到属性是“固定的”和“不变的”,那么我假设您的意思是,一旦创建了对象,您就永远不会更改它们的值。在这种情况下,应该使用以下方法:

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

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

        2
  •  10
  •   tomazy    12 年前

    另一个解决方案是使用 继承 钩子:

    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
    
        3
  •  5
  •   ceth    14 年前

    作为一个版本:

    class LibraryItem < Object
      def initialize
        @attributes = ['one', 'two'];
      end
    end
    
    class LibraryBook < LibraryItem
      def initialize
       super
       @attributes.push('three')
     end
    end
    
    b = LibraryBook.new
    
        4
  •  4
  •   Grant Goodale    14 年前

    出于好奇,这样的东西行吗?

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

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

    > Foo::ATTRIBUTES
    => ['title','authors','location'] 
    > Bar::ATTRIBUTES
    => ['title','authors','location', 'ISBN', 'pages'] 
    
        5
  •  3
  •   Nick Vanderbilt    14 年前

    activesupport在rails edge中有class_属性方法。

        6
  •  3
  •   Matt Connolly    12 年前

    要扩展@nick vanderbilt的答案,请使用active_支持,这正是我想要的这个功能的简写。下面是一个完整的示例:

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

    遗憾的是,ruby不需要一个库就很难做到这一点。这是我唯一想念的蟒蛇。在我的例子中,我不介意对活动的支持gem的依赖。

        7
  •  2
  •   andrykonchin    14 年前

    在librarybook中variable@attributes是一个新的独立变量,是objectlibrarybook的实例变量,所以它没有初始化,你会得到错误的“undefined method…为零“
    在使用

    class LibraryBook < LibraryItem
      @attributes = LibraryItem::attributes + ['ISBN', 'pages']
    end
    
        8
  •  0
  •   Wayne Walker    12 年前

    这是针对字符串(任何真正的字符串),而不是数组,但是…

    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"]
    
        9
  •  -1
  •   Salil    14 年前

    你也可以用辛斯坦茨。但没有检查。

    class LibraryItem < Object
      class << self; attr_accessor :attributes; end
      ATTRIBUTES = ['title', 'authors', 'location',]
    end
    
    class LibraryBook < LibraryItem
      ATTRIBUTES .push('ISBN', 'pages']
    end