ruby的include和extend prepend


Ruby中module的功能很像Java中的接口,在module中定义可以在多个类中复用的方法,然后在各个类中“引入”这个module,类或者类的实例就可以访问到module中定义的方法。

如何在类中“引入”module?Ruby提供了3个关键字: include, extend和prepend。

include

直接看例子:

# 定义module
module IndoorPet
    def can_be_housebroken?
        true
    end
end  

# 定义需要引入module的类
class Animal
end

class Dog < Animal
  include IndoorPet
end

class Cat < Animal
  include IndoorPet
end

class Rabbit < Animal
  include InddorPet
end

roger = Rabbit.new("Roger")
roger.can_be_housebroken? # 返回true
Rabbit.can_be_housebroken? # 抛异常 NoMethorError: undefined method 'can_be_housebroken?' for Rabbit::Class
复制代码
可以看到,通过include引入的module,module中定义的方法会作为实例方法引入,也就是类的实例可以调用,而类不可以直接调用。

通过ancestors方法打印类的祖先链:

Rabbit.ancestors # 返回 [Rabbit, IndoorPet, Animal, Object, PP::ObjectMixin, Kernel, BasicObject]
复制代码
可以看到,在Rabbit的祖先链上,IndoorPet位于Rabbit 与它的superclass之间。

extend

直接看例子:

# 省略IndoorPet的定义
class Animal 
end 

class Dog < Animal 
    extend IndoorPet
end 

class Cat < Animal
    extend IndoorPet
end 

class Rabbit < Animal 
    extend IndoorPet
end



roger = Rabbit.new("Roger")

roger.can_be_housebroken? # 抛异常 NoMethodError: undefined method `can_be_housebroken?' for  #<Rabbit:0x00007fa84389c748> 

Rabbit.can_be_housebroken? # 返回 true
复制代码
可以看到,通过extend引入的module,module中定义的方法会作为类方法引入,也就是类可以调用,但是类的实例不可调用。

通过ancestors方法打印类的祖先链:

Rabbit.ancestors # 返回 [Rabbit, Animal, Object, PP::ObjectMixin, Kernel, BasicObject]
复制代码
可以看到,通过extend方式引入的module,并不在Rabbit的祖先链上。

prepend

直接看例子:

class Animal 
end 

class Dog < Animal 
    prepend IndoorPet
end 

class Cat < Animal
    prepend IndoorPet
end 

class Rabbit < Animal 
    prepend IndoorPet
end

roger = Rabbit.new("Roger")
roger.can_be_housebroken? # 返回 true 
Rabbit.can_be_housebroken? # 抛出 NoMethodError: undefined method `can_be_housebroken?' for Rabbit:Class
复制代码
可以看到,通过prepend引入的module,module中的方法和include方式类似,也是作为类的实例方法引入的。

但是,通过ancestors方法打印类的祖先链的话,结果与include不同:

Rabbit.ancestors # 返回 [IndoorPet, Rabbit, Animal, Object, PP::ObjectMixin, Kernel, BasicObject]
复制代码
IndoorPet位于祖先链的最低部。

结论

通过上述三种方式引入module的话:

  1. include:将module中的方法作为实例方法引入,而非类方法;同时,module位于祖先连上的Class与Superclass之间。
  2. extend:将module中的类作为类方法引入,而非实例方法;同时,module不会在祖先链上出现。
  3. prepend:将module中的方法作为实例方法引入,而非类方法;同时,module位于祖先连上的最底部。
Reference


作者:danielkoo
链接:https://juejin.cn/post/7187791611042988090
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
阅读量: 691
发布于:
修改于: