LoginSignup
0
0

More than 5 years have passed since last update.

[メモ] ダックタイピングとは

Posted at

この記事は「オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方」を読んだまとめです。

ダックタイピング

ダックタイピングはいかなる特定のクラスとも結びつかないパブリックなインターフェース
「もしもオブジェクトがダックのように鳴き、ダックのように歩くのなら、そのクラスは何であれ、それはダックである」というのが名前の由来。
パブリックインターフェースをクラスから切り離して抽象的にする。
何であるかではなく、何をするかによって定義される抽象的な型をを見つける。隠された抽象的なインターフェースを見つけることがダックタイピングにつながりクラス間の依存を取り除き柔軟な設計につながる。

class Trip
attr_reader:bicycles,:customers,:vehicle

  #この'mechanic'引数はどんなクラスのものでもよい。
  def prepare(mechanic)
    mechanic.prepare_bicycles(bicycles)
  end
end

class Mechanic
  def prepare_bicycles(bicycles)
      bicycles.each{|bicycle|prepare_bicycle(bicycle)}
  end
  def prepare_bicycle(bicycle)
    #...
  end
end

Tripのprepareメソッドはprepare_bicyclesに応答できるオブジェクトに依存している。旅行の準備がメカニックにメッセージを送り自転車を準備するだけであれば問題はないが、準備工程が増えた時にこのままでは以下のようになり、Tripクラスは3つのクラス名と具体的なメソッドメソッドを知ってしまい依存してしまう。
Tripはメカニックにしかメッセージを送らないと想定していると、工程が増えたときに行き詰まってしまう。


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

class TripCoordinator
  def buy_food(customers)
    #...
  end
end

class Driver
  defgas_up(vehicle)
    #...
  end
  def fill_water_tank(vehicle)
    #...
  end
end

依存を取り除くための鍵となるのは、「Tripのprepareメソッドは単一の目的を果たすためにあるので、その引数も単一の目的を共に達成するために渡されてくるということを認識すること」です。
どの引数も同じ理由のためにここに存在し、その理由自体は引数の背後にあるクラスとは関係しません。

MechanicもDriverもTripCoordinatorもそれぞれの役割は異なるが目的は、「旅行の準備をする者」である。この発想からダックタイプを生み出すことができる。
この視点で考えればprepare_trip(旅行の準備をする)振る舞いをするのが、「旅行の準備をする者」と言い換えることができる。

抽象的な「旅行の準備をする者」というインターフェースを想定して、prepare_tripを実装しているものは「旅行の準備をする者」というダックタイプができる。MechanicもDriverもTripCoordinatorも「旅行の準備をする者」として振る舞う必要がある。


class Trip
  attr_reader:bicycles,:customers,:vehicle

  def prepare(preparers)
    preparers.each{|preparer|
      preparer.preparer_trip(self)
  end
end

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

設計をどこまで抽象的にするかはトレードオフ。ifとクラス名で分岐させて具体的にコーディングすれば必要な時間は少なく済むだろうしぱっとはわかりやすい。しかし今後の拡張、変更にコストがかかる。抽象化は手間はかかるが今後の拡張、変更は容易になる。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0