蒼時弦也
蒼時弦也
資深軟體工程師
發表於

自由的 Ruby 類別(一)

以前還在讀書的時候,常常會思考所謂的「自由」是什麼,想著以後一定要過著自由的生活。不過現實就是到了社會,依舊還是有許多限制讓你無法自由自在。

不過,在 Ruby 中的類別卻是非常自由的。

有稍微接觸過 Ruby 的人應該都知道幾個特性:

  • 物件導向語言
  • 一切都是物件

Class

在 Ruby 裡面,所有的類別的類別都是 Class 這個類別。

1Object.class
2# => Class
3
4Kernel.class
5# => Module
6Module.class
7# => Class

即使是 Module (模組) 也是屬於類別的一種,也就是說萬物都源自於 Class 這個類別。

至於 Class.class 還是會得到 Class 自己本身

Anonymous Class

至於一個類別是怎樣出生的呢?我們可以先從沒有名字的類別來看。

1Class.new
2# => #<Class:0x007f85fc30ea88>

現在我們得到了一個新的類別(物件)而且也可以對他進行一個產生實例的動作。

klass = Class.new
klass.new
# => <#<Class:0x007f85fc30ea88>:0x007f85fc2efa98>

因為這種類別很可憐,他連名字都沒有,所以他的名字就是物件本身的 #<Class:0x007f85fc30ea88>

那麼,該怎麼讓他們獲得名字呢?

1Klass = Class.new
2Klass.new
3# => #<Klass:0x007f85fc2d7510>

實際上,所有類別的名字都是一個常數。 所以才會在撰寫類別名稱的時候,必須以大寫開頭(定義為常數)才能運作。

不過,類別的繼承跟方法該怎麼辦呢?

1Klass = Class.new(Object) do
2  def echo(text)
3    puts text
4  end
5end
6
7Klass.new.echo 'Hello World'
8# => Hello World

實際上就是把一個 Block 在初始化類別的時候傳進去,讓他做一些事情。

上面的類別定義等價於下面的程式碼:

1class Klass
2  def echo(text)
3    puts text
4  end
5end

所以,我們在這邊發現我們在 classend 所寫的,可能是一個 Block。

Class Methods

這個段落會討論的是 Class 類別的方法,有趣的是他剛好也是其他類別的類別方法。

物件導向中,我們知道有兩種方法的定義。

  • 類別方法 Klass.instance
  • 實例方法 Klass.new.echo

差別在於實例方法需要先把類別用 .new 初始化之後,才能存取到這些方法。

首先,我們可以先看看 Class 類別有哪些類別方法可以使用。

1Class.methods
2# => [:include, ...,:instance_exec, :__id__]

省略了一些方法,不過看到了 include 這個熟悉的關鍵字。

1class Klass
2  include Mod
3end

沒錯,我們所看到的 include 就是我們在 include 某個模組的那一個。

所以,下面的這些動作其實是等價的。

1Klass = Class.new do
2  include Mod
3end
1class Klass
2  include Mod
3end
1# class Klass; end
2# or
3# Klass = Class.new
4
5Klass.include Mod

透過以上的探索,是不是發現大多數 Rubyist 都可能接觸過的 Rails 中,有很多這樣的應用方式呢?

1class User < ApplicationRecord
2  has_many :posts
3end

從上面的例子來看 has_many 是一個類別方法,並且可能是屬於 ApplicationRecord 或者其上層的某一個物件。

總結

在 Ruby 的世界中,所有的類別其實都是 Class 這個類別的實體表現而已。 而父類別的的類別方法,則可以作為在定義新類別時的 Block 中使用,用來拓展一個類別的多樣性。

下一篇文章會來討論 has_many 的實際應用是怎樣的。