本文翻译,改编自Include vs Extend in Ruby
在类中引用一个模块,有两种方式,一种是include,另一种是extend,两种的大致区别就是:
include是给class增加一个实例方法,而extend是增加一个类方法
我们看一下小例子.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| module Foo
def foo
puts 'heyyyyoooo!'
end
end
class Bar
include Foo
end
Bar.new.foo # heyyyyoooo!
Bar.foo # NoMethodError: undefined method ‘foo’ for Bar:Class
class Baz
extend Foo
end
Baz.foo # heyyyyoooo!
Baz.new.foo # NoMethodError: undefined method ‘foo’ for #<Baz:0x1e708>
|
如你所见,include生成了一个类的实例foo方法,而extend生成了一个类本身的类方法.
Include 例子
如果你想了解更多module之间使用include共享方法,你可以阅读我另一则文章在how I added simple permissions,那篇文章中permissions模块被好几个models引入,所以可以共享方法,这就是我要说的,所以你想了解更多可以查看那篇文章
一个普遍的共识
虽然include是增加一个实例方法,但是有一个普遍的共识,你以后会明白在ruby中使用include即可以增加类方法也可以增加实例方法,原因就是include有一个self.included的钩子方法,你可以用它来修改类中对于模块的引入,据我所知,extend没有钩子方法(这是作者错误的想法,见下面说明),不过这是有争议的,但是因为经常使用所以我想我会提到它,让我们看个小例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| module Foo
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def bar
puts 'class method'
end
end
def foo
puts 'instance method'
end
end
class Baz
include Foo
end
Baz.bar # class method
Baz.new.foo # instance method
Baz.foo # NoMethodError: undefined method ‘foo’ for Baz:Class
Baz.new.bar # NoMethodError: undefined method ‘bar’ for #<Baz:0x1e3d4>
|
但是rails现在引入了ActiveSupport::Concern,上面的方法就可以写成这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| module Foo
extend ActiveSupport::Concern
module ClassMethods
def bar
puts 'class method'
end
end
def foo
puts 'instance method'
end
end
class Baz
include Foo
end
Baz.bar # class method
Baz.new.foo # instance method
Baz.foo # NoMethodError: undefined method ‘foo’ for Baz:Class
Baz.new.bar # NoMethodError: undefined method ‘bar’ for #<Baz:0x1e3d4>
|
说明
上面原作者说extend没有hook是错误的,extend其实有一个叫self.extended的方法,作用和include中的self.included是差不多的,可看下面的例子
1
2
3
4
5
6
7
8
9
10
11
| module Foo
def self.extended(klass)
puts "extended by class #{klass}"
end
end
class Baz
extend Foo
end
=> extended by class Baz
|