2009-08-09 40 views
34

Tình huống: Tôi có nhiều lớp mà mỗi biến phải giữ một biến với băm cấu hình; một băm khác nhau cho mỗi lớp nhưng giống nhau cho tất cả các phiên bản của một lớp.Ruby: Thừa kế mã hoạt động với các biến lớp

Lúc đầu, tôi đã cố gắng như thế này

class A 
    def self.init config 
    @@config = config 
    end 

    def config 
    @@config 
    end 
end 

class B < A; end 
class C < A; end 

Nhưng chẳng mấy chốc nhận thấy rằng nó sẽ không hoạt động theo cách đó vì @@ cấu hình được tổ chức trong bối cảnh của A, không B hoặc C, như sau:

B.init "bar" 
p B.new.config # => "bar" 
p C.new.config # => "bar" - which would be nil if B had it's own @@config 

C.init "foo" 
p B.new.config # => "foo" - which would still be "bar" if C had it's own @@config 
p C.new.config # => "foo" 

tôi nghĩ của việc sử dụng nó như thế này:

modules = [B, C] 
modules.each do |m| 
    m.init(@config[m.name]) 
end 
# ... 
B.new # which should then have the correct config 

Bây giờ, nó là rõ ràng với tôi lý do tại sao điều đó xảy ra, nhưng tôi không chắc chắn về thứ e lý do cho nó được như thế này.

Không thể nó hoạt động theo cách khác, giữ biến lớp trong ngữ cảnh của lớp con?

Điều tôi cũng thấy khó chịu là thực tế rằng bản thân luôn là lớp con ngay cả khi được gọi là 'trong' lớp cha. Từ đây, đầu tiên tôi dự kiến ​​mã từ lớp cha là "được thực thi trong ngữ cảnh" của lớp con.

Một số chứng ngộ về điều này sẽ được đánh giá cao.

Mặt khác, tôi có thể phải chấp nhận nó hoạt động theo cách đó và tôi phải tìm cách khác để thực hiện việc này.

Có cách nào "meta" để thực hiện việc này không? (Tôi đã thử với class_variable_set, v.v. nhưng không có may mắn)

Hoặc có thể toàn bộ ý tưởng về phương pháp 'init' đó không đúng lúc đầu và có một số "mẫu" khác để làm điều này?

Tôi chỉ có thể làm cho @@ config một băm, giữ tất cả các cấu hình và luôn chọn đúng, nhưng tôi thấy rằng một chút khó xử .. (không phải là thừa kế ở đó để giải quyết loại vấn đề?;)

Trả lời

94

Các @@variables không phải là biến lớp. Chúng là biến phân cấp lớp, tức là chúng được chia sẻ giữa toàn bộ phân cấp lớp, bao gồm tất cả các lớp con và tất cả các phiên bản của tất cả các lớp con. (Nó đã được gợi ý rằng một trong những nên suy nghĩ của @@variables nhiều hơn như $$variables, bởi vì họ thực sự có nhiều điểm chung với $globals hơn với @ivars.Đó là cách ít nhầm lẫn.)

Ruby không có biến lớp theo nghĩa là, Java, (nơi chúng được gọi là trường tĩnh) có chúng. Nó không cần biến lớp, bởi vì các lớp cũng là các đối tượng và do đó chúng có thể có các biến số dụ giống như bất kỳ đối tượng nào khác. Tất cả những gì bạn phải làm là loại bỏ các số không liên quan @ s. (Và bạn sẽ phải cung cấp một phương thức truy cập cho biến cá thể lớp.)

class A 
    def self.init config 
    @config = config 
    end 

    def self.config # This is needed for access from outside 
    @config 
    end 

    def config 
    self.class.config # this calls the above accessor on self's class 
    end 
end 

Hãy đơn giản hóa này một chút, vì A.config rõ ràng chỉ là một attribute_reader:

class A 
    class << self 
    def init config 
     @config = config 
    end 

    attr_reader :config 
    end 

    def config 
    self.class.config 
    end 
end 

Và, trên thực tế, A.init chỉ là một nhà văn với một tên hài hước, vì vậy chúng ta hãy đổi tên nó để A.config= và biến nó thành một nhà văn, điều này có nghĩa là cặp phương thức của chúng ta giờ đây chỉ là một cặp truy cập. (Kể từ khi chúng tôi thay đổi API, các mã kiểm tra phải thay đổi là tốt, rõ ràng.)

class A 
    class << self 
    attr_accessor :config 
    end 

    def config 
    self.class.config 
    end 
end 

class B < A; end 
class C < A; end 

B.config = "bar" 
p B.new.config # => "bar" 
p C.new.config # => nil 

C.config = "foo" 
p B.new.config # => "bar" 
p C.new.config # => "foo" 

Tuy nhiên, tôi không thể rung cảm giác rằng có một cái gì đó về cơ bản iffy thêm về thiết kế, nếu bạn cần điều này ở tất cả.

+0

Tôi không thấy thiết kế này có vẻ như thế nào. Nó có vẻ như một điều đủ hợp lý để làm nói chung. – Chuck

+0

Đó chính xác là những gì tôi cần biết, cảm ơn bạn rất nhiều! :) Không thực sự biết những gì khác để nói, nó là tất cả như vậy rõ ràng bây giờ. Phương pháp init được thiết kế để thiết lập nhiều biến, tôi đã nhận được ví dụ cấu hình để đơn giản. Nhưng bây giờ bạn đề cập đến nó, nó có lẽ vẫn còn sạch hơn với accessors;) Một lần nữa, cảm ơn rất nhiều! –

+0

@Chuck: Ví dụ, có một phương thức cá thể ('A # config') không gọi phương thức cá thể cũng như truy cập trạng thái cá thể cũng không bị ghi đè. Đó có thể là một tạo tác của ví dụ được cắt xén, nó có thể là một thiết kế hợp pháp, nhưng, có lẽ, nó không phải. Ngoài ra, B và C kế thừa từ A, nhưng không ghi đè lên bất cứ điều gì, tuy nhiên, họ bằng cách nào đó dự kiến ​​sẽ có hành vi khác nhau cả từ lẫn nhau và từ A, mặc dù tất cả chúng đều giống hệt nhau. Một lần nữa: có thể hợp lý, có thể không. Tất cả phụ thuộc vào bối cảnh, tất nhiên là quá nhỏ trong ví dụ này để đạt được bất kỳ kết luận hợp lý nào. –

Các vấn đề liên quan