2013-07-09 40 views
14

Tôi đã đọc một vài bài viết về các phương thức mixin của Ruby, extendinclude và tôi vẫn chưa hoàn toàn chắc chắn về hành vi. Tôi hiểu rằng extend sẽ thêm các phương thức thể hiện của mô-đun đã cho làm phương thức singleton cho mô-đun đang mở rộng và include về cơ bản sẽ thêm nội dung của mô-đun (phương thức, hằng số, biến) vào mô-đun. chúng trong máy thu.Ruby mixins: mở rộng và bao gồm

Tuy nhiên, sau khi một số tinkering, cố gắng để có được một cảm giác về cách hành vi sẽ biểu hiện, tôi đã có một vài câu hỏi. Đây là thiết lập thử nghiệm của tôi:

module Baz 
    def blorg 
    puts 'blorg' 
    end 
end 

module Bar 
    include Baz 
    def blah 
    puts 'blah' 
    end 
end 

module Foo 
    extend Bar 
end 

class Bacon 
    extend Bar 
end 

class Egg 
    include Bar 
end 

Vì vậy, như tôi mong đợi, mô-đun Bar tăng các phương pháp dụ quy định tại Baz (#blorg), nếu như họ muốn được định nghĩa trong tự do phương pháp bao gồm, và lớp Bacon lợi nhuận các phương thức singleton Bacon::blahBacon::blorg theo tiện ích.

Bacon.blah # => blah 
Bacon.blorg # => blorg 

Và lớp Egg tăng các phương pháp quy định tại Bar (#blah và bây giờ #blorg) như các phương pháp dụ.

Egg.new.blah # => blah 
Egg.new.blorg # => blorg 

Tôi nhận được tất cả những điều đó, vì vậy tốt.

Tuy nhiên, tôi không hiểu các câu trả lời tôi nhận được từ việc sử dụng các phương pháp #ancestors#is_a?.

Bacon.ancestors # => [Bacon, Object, Kernel, BasicObject] 
Bacon.is_a? Bar # => true 

Egg.ancestors # => [Egg, Bar, Baz, Object, Kernel, BasicObject] 
Egg.is_a? Bar # => false 

Có vẻ như rằng việc mở rộng một mô-đun làm cho #is_a? phương pháp để trở true khi được hỏi về mô-đun đó, nhưng nó không được thêm vào tổ tiên của lớp, và ngược lại liên quan đến bao gồm: tổ tiên của lớp chứa các mô-đun được bao gồm, nhưng phương thức #is_a? trả về false khi được truy vấn. Lý do tại sao điều này xảy ra?

+0

+1 cho định dạng tuyệt vời của câu hỏi này. – sargas

Trả lời

24

Sự khác biệt là include sẽ thêm lớp được bao gồm vào tổ tiên của lớp bao gồm, trong khi extend sẽ thêm lớp mở rộng vào tổ tiên của lớp đơn lớp mở rộng. Phew.Hãy đầu tiên quan sát những gì xảy ra:

Bacon.ancestors 
#=> [Bacon, Object, Kernel, BasicObject] 

Bacon.singleton_class.ancestors 
#=> [Bar, Baz, Class, Module, Object, Kernel, BasicObject] 

Bacon.new.singleton_class.ancestors 
#=> [Bacon, Object, Kernel, BasicObject] 

Bacon.is_a? Bar 
#=> true 

Bacon.new.is_a? Bar 
#=> false 

Và cho lớp Egg

Egg.ancestors 
#=> [Egg, Bar, Baz, Object, Kernel, BasicObject] 

Egg.singleton_class.ancestors 
#=> [Class, Module, Object, Kernel, BasicObject] 

Egg.new.singleton_class.ancestors 
#=> [Egg, Bar, Baz, Object, Kernel, BasicObject] 

Egg.is_a? Bar 
#=> false 

Egg.new.is_a? Bar 
#=> true 

Vậy điều gì foo.is_a? Klass thực sự làm là để kiểm tra xem foo.singleton_class.ancestors chứa Klass. Một điều khác xảy ra là tất cả tổ tiên của một lớp trở thành tổ tiên của một lớp singleton của cá thể khi cá thể được tạo ra. Vì vậy, điều này sẽ đánh giá đúng cho tất cả các phiên bản mới được tạo của bất kỳ lớp nào:

Egg.ancestors == Egg.new.singleton_class.ancestors 

Vậy điều này có nghĩa là gì? extendinclude làm điều tương tự về mức độ khác nhau, tôi hy vọng các ví dụ sau đây làm cho điều này rõ ràng là cả hai cách để mở rộng một lớp cơ bản là tương đương:

module A 
    def foobar 
    puts 'foobar' 
    end 
end 

class B 
    extend A 
end 

class C 
    class << self 
    include A 
    end 
end 

B.singleton_class.ancestors == C.singleton_class.ancestors 
#=> true 

nơi class << self chỉ là cú pháp kỳ lạ để đến lớp singleton . Vì vậy, extend thực sự chỉ là viết tắt của include trong lớp singleton.

0
Egg.is_a? Egg # => false 

Bao gồm (hiệu quả) thay đổi phiên bản của lớp Egg. Mặc dù nó không phải là hoàn toàn giống nhau, nó rất giống với làm một cái gì đó giống như

class Egg < Bar 
end 

Khi mở rộng sẽ thêm các phương pháp lớp học, vì vậy đây là rất tương tự như làm một cái gì đó giống như

class Bacon 
    class << self 
    include Bar 
    end 
end 

Bạn có thể nghĩ về nó như bao gồm các thay đổi các cá thể của lớp, khi mở rộng thực sự thay đổi lớp.

+0

có lẽ bạn đã gõ sai một cái gì đó, nhưng khi tôi làm 'Egg.new.is_a? Egg' nó trả về 'true'. Ý bạn là 'Egg.is_a? Trứng # => sai'? – DesAdams

+0

Vâng tôi đã làm. Tôi sẽ chỉnh sửa nó. – Olives

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