Sự khác biệt giữa bao gồm và mở rộng trong Ruby là gì?


415

Chỉ cần nhận được đầu của tôi xung quanh Ruby siêu lập trình. Các mixin / mô-đun luôn quản lý để làm tôi bối rối.

  • bao gồm : trộn trong các phương thức mô-đun được chỉ định làm phương thức cá thể trong lớp đích
  • mở rộng : trộn trong các phương thức mô đun đã chỉ định làm phương thức lớp trong lớp đích

Vì vậy, sự khác biệt lớn chỉ là điều này hay là một con rồng lớn hơn đang ẩ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!"

Câu trả lời:


249

Những gì 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 Klazzvà mô-đun Mod, bao gồm Modtrong các Klazztrường hợp cung cấp Klazzquyền truy cập vào Modcác phương thức. Hoặc bạn có thể mở rộng Klazzbằng Modcách cấp cho lớp Klazz quyền truy cập vào Modcác phương thức. 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 Modcác phương thức mặc dù tất cả các đối tượng khác có cùng lớp là okhông.


324

mở rộng - thêm các phương thức và hằng số của mô-đun được chỉ định vào siêu dữ liệu của mục tiêu (ví dụ: lớp singleton), vd

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

bao gồm - Theo mặc định, nó trộn trong các phương thức của mô đun được chỉ định làm phương thức thể hiện trong mô đun / lớp đích. ví dụ

  • nếu bạn gọi class Klazz; include Mod; end;, bây giờ tất cả các phiên bản của Klazz đều có quyền truy cập vào các phương thức của Mod (dưới dạng phương thức ví dụ)
  • include là một phương thức riêng tư, vì nó dự định được gọi từ bên trong lớp / mô đun chứa.

Tuy nhiên , các mô-đun rất thường ghi đè include hành vi của khỉ bằng cách vá includedphương thức. Điều này rất nổi bật trong mã Rails kế thừa. biết thêm chi tiết từ Yehuda Katz .

Thông tin chi tiết khác 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 đưa vào Klazz hoặc một trong những tổ tiên của nó, câu lệnh bao gồm không có hiệu lực
  • Nó cũng bao gồm các hằng số Mod ở Klazz, miễn là họ không đụng độ
  • Nó cho phép Klazz truy cập vào các biến mô-đun của Mod, ví dụ @@foohoặ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 supertrong Klazz # foo sẽ kiểm tra Mod # foo trước khi kiểm tra theo phương pháp foo của siêu lớp thực của Klazz. Xem RubySpec để biết chi tiết.).

Tất nhiên, tài liệu cốt lõi của ruby luôn là nơi tốt nhất để thực hiện những điều này. Dự án RubySpec cũng là một tài nguyên tuyệt vời, bởi vì họ đã ghi lại chính xác chức năng.


22
Tôi biết đây là bài viết khá cũ, nhưng sự rõ ràng 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 cho một lời giải thích tốt đẹp.
MohamedSanaulla

2
@anwar Rõ ràng, nhưng bây giờ tôi có thể bình luận và tôi đã tìm thấy bài viết một lần nữa. Nó có sẵn ở đây: aaronlasseigne.com/2012/01/17/explained-include-and-extend và tôi vẫn nghĩ rằng lược đồ làm cho sự hiểu biết dễ dàng hơn nhiều
systho

1
Chiến thắng lớn trong phản hồi này là làm thế nào extendcó thể áp dụng các phương thức như các phương thức lớp hoặc thể hiện, tùy thuộc vào việc sử dụng. Klass.extend= phương thức lớp, objekt.extend= phương thức thể hiện. Tôi luôn luôn (sai) giả định các phương thức lớp đến từ extendvà ví dụ từ include.
Frank Koehl

16

Đúng rồi.

Đằ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):

Việc triển khai mặc định của Ruby là thêm các hằng số, phương thức và các biến mô-đun của mô-đun này vào 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ó.


5

Khi bạn includemô-đun vào một lớp, các phương thức mô-đun được nhập dưới dạng phương thức thể hiện .

Tuy nhiên, khi bạn extendmô-đun vào một lớp, các phương thức mô-đun được nhập dưới dạng phương thức lớp .

Ví dụ: nếu chúng ta có một mô-đun Module_testđược xác định như sau:

module Module_test
  def func
    puts "M - in module"
  end
end

Bây giờ, cho includemô-đun. Nếu chúng ta định nghĩa lớp Anhư sau:

class A
  include Module_test
end

a = A.new
a.func

Đầu ra sẽ là : M - in module.

Nếu chúng ta thay thế dòng include Module_testbằng extend Module_testvà chạy lại mã, chúng ta sẽ gặp lỗi sau : undefined method 'func' for #<A:instance_num> (NoMethodError).

Thay đổi cuộc gọi phương thức a.functhành A.func, đầu ra thay đổi thành : M - in module.

Từ thực thi mã ở trên, rõ ràng rằng khi chúng ta includemột mô-đun, các phương thức của nó trở thành các phương thức cá thể và khi chúng ta extendlà một mô-đun, các phương thức của nó trở thành các phương thức lớp .


3

Tất cả các câu trả lời khác đều tốt, bao gồm mẹo để tìm hiểu về 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 các trường hợp sử dụng:

Nếu bạn bao gồm mô-đun Tái sử dụng mô-đun trong lớp ClassThatInc bao gồm , các phương thức, hằng, lớp, mô hình con và các khai báo khác sẽ được tham chiếu.

Nếu bạn mở rộng lớp ClassThatExtends với mô-đun Tái sử dụng mô-đun, thì các phương thức 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 nhân đôi các định nghĩa động.

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


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 sửa.

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

class A
include MyMOd
end

a = A.new
a.some_method

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

Khi chúng ta sử dụng, extendchúng ta sẽ thêm liên kết vào một mô-đun trong lớp eigen của đối tượng. Vì vậy, nếu chúng ta sử dụng A.new.extend (MyMod) chúng tôi có thêm liên kết đến mô-đun của chúng tôi đến lớp dụ eigen hoặc của một a'lớp. 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à đối tượng) eigenclass A'.

vì vậy đường dẫn tra cứu phương thức cho anhư sau: a => a '=> các mô đun được liên kết với một' class => A.

cũng có một phương thức trả trước thay đổi đường dẫn tra cứu:

a => a '=> mô-đun được chuẩn bị trước A => A => bao gồm mô-đun cho A

Xin lỗi vì tiếng Anh của tôi không tốt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.