5章
ダックタイピングとは?
どのクラスとも結びつかないパブリックインターフェース。
クラスをまたぎ、オブジェクトのクラスではなく振る舞いによって定義される。
もしオブジェクトがダック(アヒル)のように鳴き、ダックのように歩くならば、そのクラスが何であれ、それはダックである。
ダックタイピングの見つけ方
クラスで分岐するcase文
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Trip
attr_reader :bicycles, :customers, :vehicle
def prepare(preparers)
preparers.each{ |preparer|
case preparer
when Mechanic
preparer.prepare_bicycles(bicycles)
when TripCoordinator
preparer.buy_food(customers)
when Driver
preparer.gas_up(vehicle)
preparer.fill_water_tank(vehicle)
end
}
end
end
|
preparers
は共通のものを共有しているのでは?
「引数のそれぞれから望むものはなんだろう」
prepare
メソッドは、引数preparersが旅行の準備をすることを望む。
kind_of?
とis_a?
1
2
3
4
5
6
7
8
9
10
|
def prepare(preparers)
if preparer.kind_of?(Mechanic)
preparer.prepare_bicycle(bicycles)
elsif preparer.kind_of?(TripCoordinator)
preparer.buy_food(customer)
elsif preparer.kind_of?(Driver)
preparer.gas_up(vehicle)
preparer.fill_water_tank(vehicle)
end
end
|
kind_of?
とis_a?
は同じ。
やっていることはクラスで分岐しているのと同じなので、同様の問題点がある。
responds_to?
どのメソッドに呼応できるかで分岐する。
1
2
3
4
5
6
7
8
9
10
|
def prepare(preparers)
if preparer.respond_to?(:prepare_bicycles)
preparer.prepare_bicycle(bicycles)
elsif preparer.respond_to?(:buy_food)
preparer.buy_food(customer)
elsif preparer.respond_to?(:gas_up)
preparer.gas_up(vehicle)
preparer.fill_water_tank(vehicle)
end
end
|
よいコード
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
|
class Trip
attr_reader :bicycles, :customers, :vehicle
def prepare(preparers)
preparers.each do |preparer|
preparer.prepare_trip(trip)
end
end
...
end
# すべての準備者(Preparer)は`prepare_trip`に応答するダック
# →以下のクラスは`prepare_trip`メソッドを持つ
class Mechanic
def prepare_trip(trip)
trip.vehicles.each do |bicycle|
prepare_bicycle(bicycle)
end
end
...
end
class TripCoordinator
def prepare_trip(trip)
buy_food(trip.customers)
end
...
end
class Driver
def prepare_trip(trip)
vehicle = trip.vehicle
gas_up(vehicle)
fill_water_tank(vehicle)
end
...
end
|
元々prepare
はMechanicやTripCoordinatorなどの具象クラスに依存していたが、これによってダックタイプに依存するようになった。
が、ダックタイプは抽象的なので、テストを書くことによって文書化する。
Rubyのコアクラスなど上位のクラスに依存している場合は、必ずしもダックタイプをする必要はない。