This page looks best with JavaScript enabled

『オブジェクト志向設計実践ガイド』5章

 ·  ☕ 2 分で読めます

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のコアクラスなど上位のクラスに依存している場合は、必ずしもダックタイプをする必要はない。

Share on

aiandrox
Written by
aiandrox
今日も楽しく明日も楽しく

目次