老徐

Never underestimate your power to change yourself!

Ruby中include和extend的比较

| Comments

本文翻译,改编自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

Comments