Ruby module: Include、extend 和 prepend


Posted by Wendy on 2021-08-15

前言

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

prependinclude 有點像,都是讓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的方法,也幫助我自己釐清一些觀念。


#ruby #module #include #extend #prepend







Related Posts

巨量資料 & 機器學習基礎教學

巨量資料 & 機器學習基礎教學

Office Tool Plus 設定

Office Tool Plus 設定

Pyside6-Designer

Pyside6-Designer


Comments