終於來到呱呱的主題啦!
今天導演就不拍情境,直接進入主題囉!
首先我們先來談談 鴨子型別(Duck Typing) 的定義是什麼
當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。
好! 今天可以結束了...
當然不可能這麼簡單用一句話結束這回合囉!
不知道大家還記得我們前幾天其實都有提到鴨子型別這關鍵字,有碰到一點程式碼,也點到一點概念。
所以我想今天就從課本中的例子,來解釋會更為完整且詳細~
class Mechanic
def prepare_bicycles(bicycle)
#do something
end
end
class TripCoordinator
def buy_food(customers)
#do something
end
end
class Driver
def gas_up(vehicle)
#do something
end
def fill_water_tank(vehicle)
#do something
end
end
class Trip
arrt_reader :bicycles, :customers, :vehicle
def prepare(prepares)
prepares.each do |prepare|
if prepare.kind_of?(Mechanic)
prepare.prepare_bicycles(bicycle)
elsif prepare.kind_of?(TripCoordinator)
prepare.buy_food(customers)
elsif prepare.kind_of?(Driver)
prepare.gas_up(vehicle)
prepare.fill_water_tank(vehicle)
end
end
end
end
就目前為止,一切或許都還很正常,但如果有天增加新的準備事項時,就又要再寫入更多的elsif
,又或者說類別名稱更改呢? 像是把Driver
改成Chauffeur
時,又要回頭修改程式碼。
那有沒有個辦法是prepares
內所擁有的還都是物件Mechanic TripCoordinator Driver
,然後跑迴圈時可以不經過判斷,自動幫我做好所有的準備呢?
或許我這樣解釋太複雜,但白話文就是我不管你是誰,當我要你準備,你就是準備好所有東西
反觀現在的程式碼,則是在Trip
內,去一個個跟每個類別說,你應該要準備什麼,這概念是完全不同的!
class Mechanic
def prepare_trip(trip)
trip.bicycles.each { |bicycle| prepare_bicycle(bicycle) }
end
end
class TripCoordinator
def prepare_trip(trip)
buy_food(trip.customers)
end
end
class Driver
def prepare_trip(trip)
vechicle = trip.vehicle
gus_up(vehicle)
fill_water_tank(vehicle)
end
end
class Trip
arrt_reader :bicycles, :customers, :vehicle
def prepare(prepares)
prepares.each do |prepare|
prepare.prepare_trip(self)
end
end
end
使用鴨子型別後,Trip
就不用去管其他類別需要準備什麼了,而是當我呼叫到其中一個類別時(例如Mechanic
),那他就可以完全的把腳踏車準備好!
而整個流程的細節在於,使用抽象介面(prepare
),因為這個介面不會去管你丟入什麼樣的物件,只要確保丟入的物件可以實現prepare_trip
的方法,這也就是為什麼~需要去準備內容的類別內,也會開放prepare_trip
的公共介面來給Trip
使用。
再來就是丟入的參數是self
,這裡之所以可以作為參數,是因為使用prepare
方法時,一定會new
出一個Trip
物件(trip = Trip.new(bicycles, customers, vehicle)
)再來執行方法(trip.prepare(prepares)
),所以這時prepare
方法內的self
就會是Trip.new(bicycles, customers, vehicle)
或者也可以說呼叫prepare
方法就是Trip.new(bicycles, customers, vehicle)
而誰呼叫,誰就是那一個self
。理所當然的~ self
參數帶入每個類別時,就可以呼叫裡面的各個數值(例如trip.customers
)!
這個抽象介面其實有一個更為精確的字可以形容:多態性(Polymorphism)
其中定義其實很好理解
在OOP中,多態性是指許多不同物件回應相同訊息的能力。訊息的傳送者不需要關心接收者類別,接收者會提供自己所擁有的特定行為版本
以我們例子來說,prepare
方法具有多態性,可以輸入不同物件(Mechanic TripCoordinator Driver
)來提供自己擁有的特地行為(例如Mechanic
提供prepare_bicycle(bicycle)
)。
至於怎麼找出鴨子型別然後加以設計呢... 我覺得就是出現判斷式時,可能就要多加注意是否藏著可以抽離的抽象介面!
好吧! 今天又過一天啦~ 接下來要討論的會是繼承等相關內容,一起加油吧!
感謝大家 如有問題,再煩請大家指教!