読者です 読者をやめる 読者になる 読者になる

「オブジェクト指向設計実践ガイド」 第5章 ダックタイピングでコストを削減する

第5章 ダックタイピングでコストを削減する

ダックタイピングとは、暗黙的なパブリックインターフェース。オブジェクトの振る舞いは、コードとして存在する明示的なインターフェースではなく、「どのメッセージに応答するか」という暗黙的なインターフェースに依存することになる。

ダックタイピングの理解

あるオブジェクトにメッセージを送ろうとするとき、そのオブジェクトの型を知っている必要はない。重要なのは、そのメッセージに応答可能かどうかだけである。オブジェクトが何で「ある」かではなく何を「する」かに注目する。

ダックタイピングによって、簡潔な記述ときれいな構造を両立することもできれば、理解不能な混沌を作り出すこともできる。コードとしてインターフェースを定義しなくて良いということは、好きにして良いということではない。クラスやインターフェース(Java)と同様に設計し、ドキュメントを整備する必要がある。

すべてがコード上に現れる具象的なコードは理解しやすいが拡張しにくい。(ダックタイピングのように)暗黙的な取り決めがある抽象的なコードは拡張性は高いがひとめで理解しにくい。このトレードオフが常に存在する。

Rubyのようにダックタイピングが使えない場合にポリモーフィズムを実現するには継承、Mix-inなどがある。

ダックタイピングの利用

ダックタイピングを使うべきはどんなときか、いくつかパターンが存在する。

ダックタイピングを使えないか検討すべき項目

  • クラスで分岐するcase文
  • kind_a?, is_a?
  • responds_to?

これらが現れたとき、なにか「共通のもの」がそこには存在するはず。

def start(args)
  args.each do |a|
    case a
    when Bar
      a.run
    when Baz
      a.init
    when Qux
      a.ignite
    end
end

start([bar, baz, qux])

Bar, Baz, Quxはどれも何かを始めるためのメソッドを持っているが、それらすべてばらばらの名前になっているので、呼び出す側がそれぞれのクラスの実装を知っている必要があった。しかしBar, Baz, Quxがすべて start というパブリックメソッドを実装している(暗黙的に"Startable"インターフェースを実装している)とすれば、こうできる。

def start(args)
  args.each(&:start)
end

start([bar, baz, qux])