前言
Ruby除了可以利用繼承(inheritance)來組織程式碼,讓程式可重複使用外,我們也可以利用引入module的方式讓class更加有彈性,這篇文章就是比較三種方式 include
extend
prepend
有什麼差別。
class的繼承鏈
在開始之前,我們先來了解一般class的繼承鏈吧,我們可以用 ancestors
這個method來查看:
class Animal
end
Animal.ancestors
=> [Animal, Object, Kernel, BasicObject]
include
當使用 include
引入module時,會將module所有的methods變成class的instance methods
module Ability
def eat
puts "Eat food."
end
end
class Animal
include Ability
end
dog = Animal.new
dog.eat
=> Eat food.
這時候,我們來看看繼承鏈有什麼變化
Animal.ancestors
=> [Animal, Ability, Object, Kernel, BasicObject]
可以看到,Ability是在Animal的上一層,所以,如果在Animal中也有相同名字的method時,會直接執行Animal中的method,那如果同時也要使用module的method,就必須用 super
來執行
class Animal
include Ability
def eat
puts "Animal eat."
end
end
Animal.new.eat
=> Animal eat.
class Animal
include Ability
def eat
puts "Animal eat."
super
end
end
Animal.new.eat
=> Animal eat.
Eat food.
extend
extend
則是會將module所有的methods變成class method
module Ability
def eat
puts "Eat food."
end
end
class Animal
extend Ability
end
Animal.eat
=> Eat food.
這時候的繼承鍊沒有變化,但如果我們去觀察 singleton_class
的繼承鍊,就可以看出端倪了
Animal.ancestors
=> [Animal, Object, Kernel, BasicObject]
Animal.singleton_class.ancestors
=> [#<Class:Animal>, Ability, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
prepend
prepend
和 include
有點像,都是讓class可以引用module的method當作instance method
module Ability
def eat
puts "Eat food."
end
end
class Animal
prepend Ability
end
Animal.new.eat
=> Eat food.
那他們差別在哪裡呢?其實是繼承鍊的順序
Animal.ancestors
=> [Ability, Animal, Object, Kernel, BasicObject]
這次可以看到 Ability
是在 Animal
的前面,這就會影響到如果有相同method時,哪一個method會執行的優先順序,還有如果兩個要都執行時,該把 super
放在哪裡
module Ability
def eat
puts "Eat food."
end
end
class Animal
prepend Ability
def eat
puts "Animal eat."
end
end
Animal.new.eat
=> Eat food.
# super 放置的位置要改放在module
module Ability
def eat
puts "Eat food."
super
end
end
class Animal
include Ability
def eat
puts "Animal eat."
super
end
end
Animal.new.eat
=> Eat food.
Animal eat.
以上就是很簡單的比較一下三種引用module的方法,也幫助我自己釐清一些觀念。