懺悔
数か月前までリスコフの置換原則に対して誤った理解をしていたので、ここに懺悔します。自分のような誤りを犯すいないともいますが、誰か一人でも理解の助けになれば幸いです。
誤っていた理解
私がリスコフの置換原則を初めて出会ったのは、「Clean Architecture 達人に学ぶソフトウェアの構造と設計」という本の中です。この本ではリスコフの置換原則を以下のように説明していました。
S型のオブジェクト o1 の各々に、対応するT型のオブジェクト o2 が1つ存在し、Tを使って定義されたプログラムPに対して o2 の代わりに o1 を使ってもPの振る舞いが変わらない場合、SはTの派生型であるといえる。
この説明を始めてみた時何が言いたいのか理解できませんでした。ただ文脈から何となくサブクラスはスーパークラスと同じ振る舞いをしないといけないと理解しました。この「同じ振る舞いをしないといけない」というのは本来プロパティやメソッドなどクラスの要素に対しての話なのですが、私はクラス全体(型)を同じにしないといけないと曲解してしまいました。
図にすると以下のようなイメージで、子クラスは親クラスと同じ要素を持たないといけなくて、親クラスが持っていない要素を持つにはprivateで持つしかないという理解です。
このような理解をしていたため、ポリモーフィズムは概念が完全に同じで、詳細の動きだけ違いうといった局所的なところでしか利用できないと勘違いをしており、いまいちパワーを理解できずにいました。
正しい理解
以下の説明にはっきり書いてあるのですが、正しい理解は「o2(親クラス)の代わりにo1(子クラス)を使っても呼び出し側の処理は変わっちゃ駄目だよね」です。子クラスは親クラスの代わりをすればいいだけなので、子クラスに新しいプロパティやメソッドが増えても関係ありません。
S型のオブジェクト o1 の各々に、対応するT型のオブジェクト o2 が1つ存在し、Tを使って定義されたプログラムPに対して o2 の代わりに o1 を使ってもPの振る舞いが変わらない場合、SはTの派生型であるといえる。
ポリモーフィズムも概念が完全に同じである必要はなく、各オブジェクトにある共通の概念を取り出し、親クラス(インターフェース)として定義することで、呼び出し側は親クラスの型に対してプログラミングを行えるので、柔軟性や可読性の高いコードになります。
このような理解に正したことで、ポリモーフィズムのパワーが分かり、実際に活用できるようになりました。
気づいたきっかけ
ある日ピクミンって凄いポリモーフィズム感あるなと考えていました。走る、集合する、攻撃するといったピクミンとしての共通する行動があるうえで、赤ピクミンは攻撃力が高かったり、紫ピクミンは力持ちだったり、同じ行動でも各色のピクミンで違った結果を持つのがポリモーフィズムの考えだと。
ただ考えていくうちに黄色ピクミンは電気を通すという行動があったり、岩ピクミンは水晶を壊すといった行動があったり、ピクミンという抽象化されたものとは別に、各色のピクミンで異なる行動があることに気が付きました。それまでの私の理解だとこの状態はリスコフの置換原則の違反になるため、継承が行えずポリモーフィズムの実現が出来なかったです。しかし各色のピクミンのオブジェクトを作成して色単位でオブジェクトの管理を行うのはとても面倒なのではとも思いました。そこで今一度リスコフの置換原則について調べなおしたら、この誤りに気付きました。
まとめ
最初に読んだ説明は確か複雑でしたが、ちゃんと読めば理解できる内容だったので、文章はちゃんと読みましょう。また周りにアーキテクチャについて会話できる人がいないので、誤った方向に進んでしまったのもあります。もう少し記事の発信やオフラインのイベントに参加して交流を持てたらと思います。