2012-10-16 36 views
5

Giả sử tôi có một mô-đun được gọi là Flight với cả phương thức lớp và phương pháp mẫu. Tôi có thể lấy phương thức của nó vào một lớp học sử dụng include, extend, hoặc cả hai:Tại sao mô-đun thừa kế Ruby không hoạt động như kế thừa lớp?

class Bat < Mammal 
    # Add Flight's class methods to Bat. 
    extend Flight 

    # Add Flight's instance methods to Bat. 
    include Flight 
    ... 
end 

include sẽ thêm Flight để Bat.ancestors, nhưng extend sẽ không.

Câu hỏi của tôi là, tại sao điều này khác với mô-đun so với các lớp học? Khi tôi phân lớp Mammal, tôi luôn luôn nhận cả hai phương thức lớp và dụ cùng một lúc. Tuy nhiên, khi tôi trộn vào một mô-đun, tôi không thể nhận cả hai phương thức lớp và dụ cùng một lúc (trừ khi tôi sử dụng móc self.included hoặc thứ gì đó như ActiveSupport::Concern).

Có vấn đề về thiết kế ngôn ngữ nào đằng sau sự khác biệt này không?

+1

Vâng, phải có một số khác biệt để biện minh cho sự tồn tại của một riêng biệt thực thể, phải không? :) –

Trả lời

3

"Khi tôi phân lớp Mammal, tôi luôn luôn có được cả hai lớp và phương pháp dụ cùng một lúc"

Đó là bởi vì Bat lớp như một đối tượng cũng thừa hưởng những phương pháp dụ từ Mammal lớp singleton.

Inheritance Graph

Bao gồm một mô-đun vào một lớp học thay đổi phương pháp tìm kiếm chuỗi. Vì vậy, trên thực tế lớp không kế thừa bất kỳ phương thức cá thể nào.

Mở rộng một lớp học với mô-đun giống như mở rộng bất kỳ đối tượng nào. Lớp này chỉ đơn giản là lấy các phương thức cá thể của mô-đun như các phương thức của lớp (ví dụ các phương thức trên chính đối tượng lớp).

3

Tôi muốn giải quyết một phần của câu hỏi của bạn:

include sẽ thêm Flight để Bat.ancestors, nhưng extend sẽ không.

mở rộng là không giống như bao gồm nên làm điều gì đó khác nhau rõ ràng ... Bạn có thể nghĩ mở rộng là tương đương với một bao gồm trên lớp metaclass.

Hãy nhìn vào ví dụ sau:

module M 
end 

class A 
    include M 
end 

# then you will see M within A's ancestors as you know 
A.ancestors # => [A, M, Object...] 


class B 
    # the following is roughly the same as extend M: 
    class <<self 
    include M 
    end 
end 

# then you will see M within B's metaclass' ancestors 
MetaclassOfB = class <<B; self; end 
MetaclassOfB.ancestors # => [M, Class, Module...] 

Vì vậy, kể từ mở rộng giống như một bao gồm trên metaclass, bạn sẽ thấy các module mở rộng hiển thị trong metaclass' chuỗi tổ tiên .. .

10

Cả Module#includeObject#extend được sử dụng để thêm phương pháp dụ của một Module một Object. Với mô-đun:

module Flight 
    def can_fly? 
     true 
    end 
end 

Module#include được sử dụng để thêm (hoặc trộn trong) các phương pháp thể hiện của một module để các phương pháp thể hiện của một lớp hoặc một module:

class Bat < Mammal 
    include Flight 
end 

a = Bat.new() 
a.can_fly?  # true 

Nó thực sự ảnh hưởng đến phương pháp Object#is_a?, vì vậy:

a.is_a? Flight  # true 

Module#include là một method private, vì vậy nó chỉ có thể được gọi với ký hiệu chức năng khi định nghĩa một lớp hoặc mô-đun khác:

class Bat < Mammal 
    self.include Flight  # NoMethodError: private method called 
end 

Object#extend thêm các phương pháp thể hiện của một mô-đun như phương pháp singleton đến đối tượng mà nó gọi, vì vậy bạn có thể làm điều này:

b = Mammal.new() 
b.extend Flight 
b.can_fly?   # true 
b.is_a? Flight  # true 

c = Mammal.new() 
c.can_fly?   # NoMethodError: undefined method 

Và chỉ b sẽ có những phương pháp dụ từ Flight; các đối tượng khác Mammal sẽ không.

Khi gọi Object#extend bên trong định nghĩa lớp, các phương thức được thêm vào eigenclass của lớp bạn đang xác định. Đây là sự khác biệt quan trọng giữa hai phương pháp khi sử dụng chúng trong một định nghĩa lớp, bởi vì các phương pháp được thêm vào như phương pháp lớp:

class Bat < Mammal 
    extend Flight 
end 

Bat.can_fly?  # true 

d = Bat.new 
d.can_fly?  # NoMethodError: undefined method 
Các vấn đề liên quan