はじめに
この記事では、オブジェクト指向の基本である ポリモーフィズム(多態性) を継承とダックタイピングの方法で説明し、それぞれの考え方の違いにも触れていきます。
ポリモーフィズムとは?
ポリモーフィズムとは、異なるクラスのオブジェクトが、同じメソッド名で異なる振る舞いをすることです。
Rubyでは、継承とダックタイピングの2つの代表的な方法でこれを実現できます。
継承によるポリモーフィズム
親クラスを使って振る舞いを統一する実装パターンです。
class Animal
def speak
raise NotImplementedError
end
end
class Dog < Animal
def speak
"ワン!"
end
end
class Cat < Animal
def speak
"ニャー!"
end
end
[Dog.new, Cat.new].each do |animal|
puts animal.speak
end
# 実行結果:
# ワン!
# ニャー!
Animal
は speak
という共通のインターフェースを定義。Dog
と Cat
はそれを実装。each
で統一的に処理できるのは、すべてのオブジェクトが Animal
を継承しているからです。
ダックタイピングによるポリモーフィズム
この方法では、共通の親クラスは必要ありません。
「もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルに違いない」
(If it walks like a duck and quacks like a duck, it must be a duck)
Rubyではこのように、クラスの継承関係よりも「何ができるか」に注目するスタイルをダックタイピングと呼びます。
たとえば、speak
というメソッドを持っていれば、どんなクラスでも同じように扱えます。
class Dog
def speak
"ワン!"
end
end
class Cat
def speak
"ニャー!"
end
end
[Dog.new, Cat.new].each do |animal|
puts animal.speak
end
# 実行結果:
# ワン!
# ニャー!
このように、Dog
も Cat
も speak
メソッドを持っているため、同じように扱うことができます。
クラスの継承関係は必要ありません。「speak
できるか?」がすべてです。
継承とダックタイピングの違い
一見すると、「ダックタイピングはAnimal
の継承がないだけ?」と思えるかもしれません。
ですが、実は設計の思想がまったく異なります。
継承:型で振る舞いを保証する設計
class Animal
def speak
raise NotImplementedError
end
end
共通の親クラスでメソッドの存在を明示的に保証。
保守性が高く、型(親クラス)に基づいて設計が統一されます。
ダックタイピング:「できること」で判断する設計
Rubyでは、クラスに関係なく speak
メソッドを持っていれば、それを「鳴けるもの」として扱います。
このように、「型」ではなく「振る舞い」で判断するのがダックタイピングです。
最後に
Rubyにおけるポリモーフィズムの実現方法として「継承」と「ダックタイピング」の2つを説明してきました。
どちらのパターンでも、Dog
や Cat
など異なるクラスのオブジェクトが、同じ speak
というメソッド名で異なる振る舞いをしていたのを覚えているでしょうか?
このように、異なる型のオブジェクトが同じメソッドを呼び出されても、それぞれのやり方で振る舞うことが、ポリモーフィズムです。
コメント