2008-10-01 35 views
344

Chỉ cần nhận được đầu của tôi xung quanh Ruby metaprogramming. Các mixin/mô-đun luôn quản lý để gây nhầm lẫn cho tôi.Sự khác nhau giữa bao gồm và mở rộng trong Ruby là gì?

  • bao gồm: hỗn hợp trong các phương pháp mô-đun quy định như phương pháp dụ trong lớp mục tiêu
  • mở rộng: hỗn hợp trong các phương pháp mô-đun quy định như phương pháp lớp trong lớp mục tiêu

Vì vậy, sự khác biệt lớn chỉ này hoặc là một con rồng lớn ẩn nấp? ví dụ:

module ReusableModule 
    def module_method 
    puts "Module Method: Hi there!" 
    end 
end 

class ClassThatIncludes 
    include ReusableModule 
end 
class ClassThatExtends 
    extend ReusableModule 
end 

puts "Include" 
ClassThatIncludes.new.module_method  # "Module Method: Hi there!" 
puts "Extend" 
ClassThatExtends.module_method   # "Module Method: Hi there!" 
+0

Kiểm tra liên kết này quá: http://juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/ – Donato

Trả lời

209

Điều bạn đã nói là chính xác. Tuy nhiên có nhiều hơn thế.

Nếu bạn có một lớp Klazz và module Mod, bao gồm Mod trong Klazz cho trường hợp của Klazz tiếp cận với phương pháp Mod 's. Hoặc bạn có thể mở rộng Klazz bằng cách Mod cấp cho các lớp lớpKlazz truy cập vào phương thức Mod của. Nhưng bạn cũng có thể mở rộng một đối tượng tùy ý với o.extend Mod. Trong trường hợp này, đối tượng riêng lẻ nhận được các phương thức của Mod mặc dù tất cả các đối tượng khác có cùng lớp là o thì không.

13

Đúng vậy.

Đằng sau hậu trường, bao gồm thực sự là một bí danh cho append_features, mà (từ các tài liệu):

thực hiện mặc định của Ruby là thêm hằng số, phương pháp, và mô-đun biến của mô-đun này aModule nếu mô-đun này chưa được thêm vào aModule hoặc một trong các tổ tiên của nó.

274

mở rộng - cho biết thêm các phương pháp và các hằng số mô-đun được chỉ định để metaclass của mục tiêu (ví dụ: lớp singleton) ví dụ

  • nếu bạn gọi Klazz.extend(Mod), bây giờ Klazz có phương pháp Mod của (như các phương pháp lớp)
  • nếu bạn gọi obj.extend(Mod), bây giờ obj có phương pháp Mod của (như các phương pháp chẳng hạn), nhưng không có trường hợp khác của obj.class có những phương pháp thêm.
  • extend là một phương pháp nào

bao gồm - Theo mặc định, nó trộn lẫn trong các phương pháp mô-đun được chỉ định như các phương pháp dụ trong các mô-đun mục tiêu/lớp. ví dụ:

  • nếu bạn gọi class Klazz; include Mod; end;, bây giờ tất cả các trường hợp của Klazz được tiếp cận với phương pháp Mod của (như các phương pháp chẳng hạn)
  • include là một phương pháp tư nhân, bởi vì nó dự định sẽ được gọi từ bên trong lớp container/module.

Tuy nhiên, mô-đun rất thường xuyên overrideinclude 's hành vi của khỉ vá phương pháp included. Điều này rất nổi bật trong mã Rails cũ. more details from Yehuda Katz.

Thông tin chi tiết về include, với hành vi mặc định của nó, giả sử bạn đã chạy đoạn mã sau

class Klazz 
    include Mod 
end 
  • Nếu Mod đã được bao gồm trong Klazz, hoặc một trong những tổ tiên của mình, bao gồm tuyên bố không có hiệu lực
  • Nó cũng bao gồm hằng số Mod trong Klazz, miễn là chúng không xung đột
  • Nó cho phép Klazz truy cập vào các biến mô-đun của Mod, ví dụ: @@foo hoặc @@bar
  • tăng ArgumentError nếu có chu kỳ bao gồm
  • Gắn các module như tổ tiên trực tiếp của người gọi (ví dụ: Nó cho biết thêm Mod cho Klazz.ancestors, nhưng Mod không được thêm vào chuỗi Klazz.superclass.superclass.superclass Vì vậy, gọi super trong Klazz # foo sẽ kiểm tra Mod # foo trước khi kiểm tra phương thức foo của siêu lớp thực sự của Klazz. Xem chi tiết của RubySpec.).

Tất nhiên, the ruby core documentation luôn là nơi tốt nhất để thực hiện những việc này. The RubySpec project cũng là một tài nguyên tuyệt vời, bởi vì chúng ghi lại chính xác chức năng.

+13

Tôi biết đây là bài đăng khá cũ, nhưng độ rõ của câu trả lời không thể ngăn tôi bình luận. Cảm ơn rất nhiều vì lời giải thích tốt đẹp. – MohamedSanaulla

+0

[RubySpec] (http://www.rubyspec.org/) đã chết: ( – wiseland

+0

@systho Liên kết đó hiện không hoạt động – Anwar

4

Tất cả các câu trả lời khác là tốt, bao gồm cả đầu để khai thác thông qua RubySpecs:

https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb

https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb

Đối với trường hợp sử dụng:

Nếu bạn bao gồm mô-đun ReusableModule trong lớp ClassThatIncludes, các phương pháp, hằng số, các lớp, submodules, và tờ khai khác được tham chiếu.

Nếu bạn mở rộng ClassThatExtends lớp với module ReusableModule, sau đó các phương pháp và hằng được sao chép. Rõ ràng, nếu bạn không cẩn thận, bạn có thể lãng phí rất nhiều bộ nhớ bằng cách tự động sao chép các định nghĩa.

Nếu bạn sử dụng ActiveSupport :: Quan tâm, chức năng .included() cho phép bạn viết lại trực tiếp lớp bao gồm. mô-đun ClassMethods bên trong một mối quan tâm được mở rộng (sao chép) vào lớp bao gồm.

1

Tôi đã học nó trước đây nhưng đánh giá cao nó khi tôi sử dụng nó. Đây là sự khác biệt:

này không làm việc nhưng sẽ làm việc nếu tôi đã xác định nó như def page_views(campaign):

class UserAction 
    include Calculations 

    def self.page_views(campaign) 
    overall_profit = calculate_campaign_profit(campaign) 
    end 
end 

này hoạt động:

class UserAction 
    extend Calculations 

    def self.page_views(campaign) 
    overall_profit = calculate_campaign_profit(campaign) 
    end 
end 
1

tôi cũng muốn giải thích cơ chế khi nó hoạt động. Nếu tôi không đúng xin vui lòng chính xác.

Khi chúng tôi sử dụng include, chúng tôi sẽ thêm liên kết từ lớp của chúng tôi vào mô-đun chứa một số phương pháp.

class A 
include MyMOd 
end 

a = A.new 
a.some_method 

Đối tượng không có phương pháp, chỉ có các mô-đun và mô-đun. Vì vậy, khi a nhận mesage some_method nó bắt đầu phương pháp tìm kiếm some_method trong lớp eigen a 's, sau đó trong A lớp và sau đó trong liên quan đến A module lớp nếu có một số (theo thứ tự ngược lại, cuối cùng bao gồm chiến thắng).

Khi chúng tôi sử dụng extend, chúng tôi sẽ thêm liên kết vào mô-đun trong lớp riêng của đối tượng. Vì vậy, nếu chúng ta sử dụng A.new.extend (MyMod), chúng ta sẽ thêm liên kết vào mô đun của chúng ta vào lớp eigen instance của A hoặc lớp a'. Và nếu chúng ta sử dụng A.extend (MyMod), chúng ta sẽ thêm liên kết vào A (đối tượng, các lớp cũng là các đối tượng) eigenclass A'.

nên con đường phương pháp tra cứu cho a được như sau: a => a '=> module liên kết với một' class => A.

cũng có một phương pháp thêm vào trước mà thay đổi con đường tìm kiếm:

a => a '=> mô-đun được thêm trước A => A => mô-đun được bao gồm A

xin lỗi vì tiếng anh xấu của tôi.

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