Learn Ruby The Hard Way

笨方法學 Ruby

習題 45: 物件、類和從屬關係

有一個重要的概念你需要弄明白,那就是 Class「類」和 Object「物件」的區別。問題在於,class 和 object 並沒有真正的不同。它們其實是同樣的東西,只是在不同的時間名字不同罷了。我用禪語來解釋一下吧:

魚(Fish)和鮭魚(Salmon)有什麼區別?

這個問題有沒有讓你有點暈呢?說真的,坐下來想一分鐘。我的意思是說,魚和鮭魚是不一樣,不過它們其實也是一樣的是不是?泥鰍是魚的一種,所以說沒什麼不同,不過泥鰍又有些特別,它和別的種類的魚的確不一樣,比如鮭魚和比目魚就不一樣。所以鮭魚和魚既相同又不同。怪了。

這個問題讓人暈的原因是大部分人不會這樣去思考問題,其實每個人都懂這一點,你無須去思考魚和鮭魚的區別,因為你知道它們之間的關係。你知道鮭魚是魚的一種,而且魚還有別的種類,根本就沒必要去思考這類問題。

讓我們更進一步,假設你有一隻水桶,裡邊有三條鮭魚。假設你的好人卡多到沒地方用,於是你給它們分別取名叫Frank,Joe,Mary。現在想想這個問題:

Mary 和鮭魚有什麼區別?

這個問題一樣的奇怪,但比起魚和鮭魚的問題來還好點。你知道 Mary是一條鮭魚,所以他並沒什麼不同,他只是鮭魚的一個「實例(instance)」。Joe 和Frank 一樣也是鮭魚的實例。我的意思是說,它們是由鮭魚創建出來的,而且代表著和鮭魚一樣的屬性。

所以我們的思維方式是(你可能會有點不習慣):魚是一個「類(class)」,鮭魚是一個「類(class)」,而 Mary 是一個「物件(object)」。仔細想想,然後我再一點一點慢慢解釋給你。

魚是一個「類」,表示它不是一個真正的東西,而是一個用來描述具有同類屬性的實例的概括性詞彙。你有鰭?你有鰾?你住在水裡?好吧那你就是一條魚。

後來一個博士路過,看到你的水桶,於是告訴你:「小伙子,你這些魚是鮭魚。」 專家一出,真相即現。並且專家還定義了一個新的叫做​​「鮭魚」的「類」,而這個「類」又有它特定的屬性。長鼻子?紅肉?體型大?住在海裡或是乾淨新鮮的水裡?吃起來味道不錯?那你就是一條鮭魚。

最後一個廚師過來了,他跟博士說:「非也非也,你看到的是鮭魚,我看到的是Mary,而且我要把 Mary 淋上美味醬料做一道小菜。 」於是你就有了一隻叫做Mary 的鮭魚的「實例(instance)」(鮭魚也是魚的一個「實例」),並且你使用了它(把它塞到你的胃裡了),這樣它就是一個​​「物件(object)」。

這會你應該了解了:Mary 是鮭魚的成員,而鮭魚又是魚的成員。這裡的關係式:物件屬於某個類,而某個類又屬於另一個類。

寫成程式碼是什麼樣子

這個概念有點詭異,不過實話說,你只要在建立和使用class的時候操心一下就可以了。我來給你兩個區分 ClassObject的小技巧。

首先針對類和物件,你需要學會兩個說法,「is-a(是啥)」和「has-a(有啥)」。「是啥」要用在談論「兩者以類的關係互相關聯」的時候,而「有啥」要用在「兩者無共同點,僅是互為參照」的時候。

接下來,通讀這段程式碼,將每一個註解為##??的位置標明他是「is-a」還是「has-a」的關係,並講明白這個關係是什麼。在程式碼的開始我還舉了幾個例子,所以你只要寫剩下的就可以了。

記住,「是啥」指的是魚和鮭魚的關係,而「有啥」指的是鮭魚和烤肉架的關係。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
## Animal is-a object (yes, sort of confusing) look at the extra credit
class Animal

end

## ??
class Dog < Animal

  def initialize(name)
    ## ??
    @name = name
  end

end

## ??
class Cat < Animal

  def initialize(name)
    ## ??
    @name = name
  end

end

## ??
class Person

  attr_accessor :pet

  def initialize(name)
    ## ??
    @name = name

    ## Person has-a pet of some kind
    @pet = nil
  end

end
## ??
class Employee < Person

  def initialize(name, salary)
    ## ?? hmm what is this strange magic?
    super(name)
    ## ??
    @salary = salary
  end

end

## ??
class Fish

end

## ??
class Salmon < Fish

end

## ??
class Halibut < Fish

end

## rover is-a Dog
rover = Dog.new("Rover")

## ??
satan = Cat.new("Satan")

## ??
mary = Person.new("Mary")

## ??
mary.pet = satan

## ??
frank = Employee.new("Frank", 120000)

## ??
frank.pet = rover

## ??
flipper = Fish.new

## ??
crouse = Salmon.new

## ??
harry = Halibut.new

加分習題

  1. 有沒有辦法把 Class 當作 Object 使用呢?
  2. 在習題中為 animals、fish、還有people 添加一些函式,讓它們做一些事情。看看當函數在 Animal 這樣的「基類(base class)」裡和在 Dog 裡有什麼區別。
  3. 找些別人的程式碼,理清裡邊的「是啥」和「有啥」的關係。
  4. 使用 Array 和 Hash 建立一些新的一對應多的「has-many」的關係。
  5. 你認為會有一種「has-many」的關係嗎?閱讀一下關於「多重繼承(multiple inheritance)」的資料,然後儘量避免這種用法。