2010-05-02 35 views
17

Tôi có một lớp Ruby được gọi là LibraryItem. Tôi muốn kết hợp với mọi cá thể của lớp này một mảng các thuộc tính. mảng này dài và trông giống nhưBiến thể hiện lớp Ruby và thừa kế

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

Lưu ý rằng những thuộc tính không thực sự coi là phương pháp, chỉ là một danh sách các thuộc tính rằng một LibraryItem có.

Tiếp theo, tôi muốn tạo một lớp con của LibraryItem được gọi là LibraryBook có một loạt các thuộc tính bao gồm tất cả các thuộc tính của LibraryItem nhưng cũng sẽ bao gồm nhiều hơn nữa.

Cuối cùng tôi sẽ muốn nhiều lớp con của LibraryItem mỗi phiên bản riêng của họ về mảng @attributes nhưng mỗi cách thêm vào LibraryItem 's @attributes (ví dụ, LibraryBook, LibraryDVD, LibraryMap, vv).

Vì vậy, đây là nỗ lực của tôi:

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

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

này không hoạt động. Tôi nhận được lỗi

undefined method `push' for nil:NilClass 

Nếu nó là để làm việc, tôi muốn một cái gì đó như thế này

puts LibraryItem.attributes 
puts LibraryBook.attributes 

để đầu ra

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

(Added 02-May-2010) Một giải pháp để làm điều này là làm cho @attributes một biến cá thể đơn giản và sau đó thêm các thuộc tính mới cho LibraryBoot trong phương thức initialize (điều này được đề xuất bởi các demas trong một o f câu trả lời). Trong khi điều này chắc chắn sẽ làm việc (và, trên thực tế, những gì tôi đã làm tất cả cùng), tôi không hài lòng với điều này vì nó là tối ưu phụ: tại sao các mảng không thay đổi này được xây dựng mỗi khi một đối tượng là tạo?

Điều tôi thực sự muốn là có các biến lớp có thể kế thừa từ một lớp cha nhưng khi thay đổi trong lớp con không thay đổi trong lớp cha.

Trả lời

7

Kể từ khi bạn đề cập rằng các thuộc tính được "cố định" và "không thay đổi", tôi giả định rằng bạn có nghĩa là bạn sẽ không bao giờ thay đổi giá trị của chúng khi đối tượng được tạo. Trong trường hợp đó, một cái gì đó như sau nên làm việc:

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

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

Bạn đang tự thực hiện một phương pháp đọc (thay vì để cho attr_accessor tạo ra nó cho bạn) nguỵ trang tên nội bộ của mảng. Trong lớp con của bạn, bạn chỉ cần gọi hàm đọc của lớp tổ tiên, gắn vào các trường bổ sung được liên kết với lớp con và trả về trường đó cho người gọi. Đối với người dùng, điều này xuất hiện như một biến thành viên chỉ đọc có tên là attributes có các giá trị bổ sung trong lớp con.

-1

Bạn cũng có thể sử dụng CINSTANTS. Không có kiểm tra mặc dù.

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

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

Đây không phải những gì là Tôi muốn. Tôi muốn biến thể hiện của lớp cho LibraryItem chỉ chứa ['title', 'authors', 'location',] trong khi cùng một biến mẫu cho LibraryBook chứa ['title', 'authors', 'location',] plus [ 'ISBN', 'trang']. Tôi sẽ chỉnh sửa câu hỏi để làm rõ hơn. – rlandster

+0

Điều này có lỗi cú pháp. Ngoài ra, thuộc tính đối tượng lớp * thuộc tính * thậm chí không được kết nối với hằng số * ATTRIBUTES *. –

5

Cũng giống như một phiên bản:

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

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

b = LibraryBook.new 
+0

Đây là, trên thực tế, làm thế nào tôi đang làm nó ngay bây giờ. Nhưng giải pháp này không phải là tối ưu từ góc độ hiệu suất. Thiết lập các thuộc tính trong phương thức khởi tạo có nghĩa là mã thuộc tính-thiết lập được chạy cho đối tượng _every_ được tạo ra. Nhưng các thuộc tính được cố định, vì vậy, về mặt lý thuyết ít nhất, nên có một cách để thiết lập các thuộc tính chỉ một lần tại thời gian biên dịch cho mỗi lớp. Tôi sẽ viết lại câu hỏi của tôi (một lần nữa) để làm cho điều này rõ ràng hơn. – rlandster

2

Trong @attributes biến LibraryBook là một biến độc lập mới, ví dụ biến của đối tượng LibraryBook, vì vậy nó không được khởi tạo và bạn nhận được lỗi "phương pháp xác định ... cho nil"
Bạn nên để khởi tạo nó bằng danh sách LibraryItem attribut trước khi sử dụng

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

Ra khỏi tò mò, sẽ giống như công việc này?

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

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

Điều này dường như để tạo ra kết quả mong muốn - mảng Attributes được mở rộng khi đối tượng lớp được tạo ra, và các giá trị của các thuộc tính khác nhau như mong đợi:

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

Tôi muốn mọi cá thể của lớp đều có các thuộc tính được chỉ định. Đề xuất của bạn định nghĩa các mảng không đổi không phải thuộc tính đối tượng. – rlandster

+0

Nó phải là '|| =' thay vì '| ='. Nhưng ngay cả sau đó nó sẽ thất bại vì hằng số ATTRIBUTES trong Bar chưa được định nghĩa, vì vậy bạn không thể sử dụng '|| =' trên nó. –

+1

@MattConnolly, không có nó không phải là '|| ='. 'Array # |' được thiết lập union. Trong ví dụ, Bar :: ATTRIBUTES trở thành liên minh của Foo :: ATTRIBUTES và mảng hai phần tử được định nghĩa trong Bar. –

3

ActiveSupport có phương thức class_attribute trong cạnh đường ray.

3

Để mở rộng câu trả lời của @Nick Vanderbilt, sử dụng active_support bạn làm điều này, đó chính xác là bàn tay ngắn tôi muốn cho chức năng này. Dưới đây là ví dụ hoàn chỉnh:

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

Xấu hổ quá khó để đạt được điều này mà không cần thư viện. Đó là điều duy nhất tôi nhớ từ python. Và trong trường hợp của tôi, tôi không nhớ phụ thuộc vào gem gem active_support.

0

này là dành cho chuỗi (bất cứ điều gì thực sự), chứ không phải là mảng, nhưng ...

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

Một giải pháp khác là sử dụng các thừa hưởng móc:

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 
Các vấn đề liên quan