PugからShibainuはうまれない(※継承の話)
※初学者さん向けです。
継承がよくわからない、という話を時々聞くので書いてみようと思った。Rubyです。
例えばイヌのパグを表現してるClassがあるとする。
class Pug def bark "ワンワン" end def tail "巻き尾" end end
さらに次にシバイヌが必要になった。すでに書いたパグと、大体特徴が一致しているので、継承を試したくなるかもしれない。
class Shibainu < Pug end shibainu = Shibainu.new puts shibainu.bark #=> "ワンワン" puts shibainu.tail #=> "巻き尾"
だが、ここで踏み留まってみてほしい。継承は親子関係とも表現される。パグから継承してシバイヌを作るというのは、親子関係というには難しい。具体的な例として、例えばパグの耳の特徴を新しく定義したとする。
class Pug def bark "ワンワン" end def tail "巻き尾" end def ears "垂れ耳" end end
このとき、パグを継承したシバイヌは、親が持っているメソッドを使えるようになる。すると、メソッドを呼び出した時に決定的な問題が表層に浮き出てくることになる。
class Shibainu < Pug end shibainu = Shibainu.new puts shibainu.ears #=> "垂れ耳"
シバの耳は一般的には立ち耳と言われているので、shibainu.ears
の戻り値は適さない。これを手っ取り早く是正するならば、Shibainuクラスの中に ears メソッドをオーバーライドすればOKだ。しかしそれでは根本の解決にはなっていない。追加で新しいクラスをPugから継承して作ろうとすると、常に「誤った結果を返すメソッドも継承される」問題もついて回るので、バグの要因になりうる。
PugとShibainuの機能をうまく取りまとめたいときは、二つの共通点としてDogというクラスを定義して、そこから継承でPugとShibainuを作るのがよい。
class Dog def bark "ワンワン" end end class Pug < Dog def ears "垂れ耳" end end class Shibainu < Dog def ears "立ち耳" end end dog1 = Pug.new puts dog1.bark #=> "ワンワン" puts dog2.ears #=> "垂れ耳" dog2 = Shibainu.new puts dog2.bark #=> "ワンワン" puts dog2.ears #=> "立ち耳"
継承を扱う時は、扱っているクラスの関係に注目して考えていくと吉。
以下、余談。
Dogの継承先へ、常に tail
ears
のメソッドを持たせることを約束させたい場合は、次のようにすると便利。
class Dog def bark "ワンワン" end def ears NotImplementedError end end class Corgi < Dog end corgi = Corgi.new corgi.tail #=> NotImplementedError