Runner in the High

技術のことをかくこころみ

Rubyにおけるポリモーフィズムとダック・タイピング

自分がOOPをそれっぽく学んだのは、サンディ・メッツの「オブジェクト指向設計実践ガイド」だが、この本だとダック・タイピングはバキバキにでてくる一方であまりポリモーフィズムについては詳しく書かれていない。thoughtbotのブログの記事、Rubyとポリモーフィズムによると、Rubyにおけるポリモーフィズムは「継承」と「ダック・タイピング」によって実装できるらしい。

継承

まずは継承のパターン。GenericParserというクラスを継承したis-a関係のJsonParserXmlParserを定義し、parseメソッドをオーバーライドしている。

class GenericParser
  def parse
    raise NotImplementedError, 'Implementation required.' 
  end
end

class JsonParser < GenericParser
  def parse
    # Jsonをパースする実装 
  end
end

class XmlParser < GenericParse
  def parse
    # XMLをパースする実装 
  end
end

parser = XmlParser.new
parser.parse

parser = JsonParser.new
parser.parse

ダック・タイピング

つぎにダック・タイピング。ダック・タイピングでは、各パーサ実装はなんのクラスも継承せず、とにかくparseというメソッドを持っているということだけが共通している。

class XmlParser
  def parse
    # Jsonをパースする実装 
  end
end

class JsonParser
  def parse
    # XMLをパースする実装 
  end
end

class GenericParser
  def parse(parser)
    parser.parse
  end
end

parser = GenericParser.new
parser.parse(XmlParser.new)
parser.parse(JsonParser.new)

実装の違い

  • 継承 のコードでは、パーサを使う部分でそれが XmlParser なのか JsonParser なのかが意識されてparseメソッドが呼び出されている。
  • ダック・タイピング のコードでは、parseメソッドを呼び出すGenericParserは、そのメソッドを持つparserが何かを意識していない。

ということが分かる。

継承によるコードは、それぞれのパーサが「誰なのか」(=なにを継承しているか)が分かることによって、パーサに何ができるか(どんなインターフェースを持っているか)を判断できる。一方で、ダック・タイピングとはすなわち「誰か」ではなく「何をするか」によってオブジェクトを見分けるため、パーサがparseメソッドを呼び出せるということを期待するだけでインターフェースは意識しない。

ダック・タイピングとポリモーフィズムの関係

「なるほど、ということはダック・タイピングはポリモーフィズムのサブセットなのだろうか?」 という疑問が湧いてくる。

これまで自分は、ポリモーフィズムとはis-a関係にある継承クラスが抽象クラスと共通したインターフェースで個々の振る舞いを特化したものに変えること、というような理解をしていた。なので、必ずしも共通したインターフェースが保証される関係ではないダック・タイピングはポリモーフィズムに該当しないと考えていたわけだ。だがWikipediaのポリモーフィズムのページによると、ダック・タイピングは動的ポリモーフィズム(英: Runtime Polymorphism)というものに該当するようだ。この意味では、ダック・タイピングは継承に並んでポリモーフィズムを実現するための手段であると言っても良いのではないかと思う。

とはいえ、Googleポリモーフィズムとダック・タイピングについて調べるとStackoverflowの質問がそこそこヒットするが、回答者によってポリモーフィズムとダック・タイピングの関係をどう捉えるかというのは割れているように見える。ある人は 「ダック・タイピングはポリモーフィズムするための方法の一つで間違いないよ」 と言ったり、またある人は ポリモーフィズムはインターフェースによる明示性(explicitness)を必要をするものだからダック・タイピングは違う」 と言ったりしている。やはり、これに関してはもう少しOOPの原典的なものにあたったほうがいいのかもしれない。