Chapter 09

第九章 ポリモーフィズムとは

kuramapommel
kuramapommel
2021.05.07に更新

「場合」を隠蔽するためのポリモーフィズム

ここまでで、 if から switchenum から interface と昇華していく流れはご理解いただけましたでしょうか

もしまだご理解いただけていないのであれば、それはぼくの書き方が悪いだけなので、お手数をおかけいたしますが、ぼくの Twitter へリプライ飛ばしていただけると嬉しいです
いただいたフィードバックをもとに書きっぷりを直します

さて、ポリモーフィズムというのは日本語で 「多態性」 と訳すそうです
聞いたことありますかこの言葉、少なくともぼくはエンジニアになるまで知らない言葉でした

多態性やポリモーフィズムを Wikipedia 先生 に問いかけてみると、下記のように書かれています

ポリモーフィズム(英: Polymorphism)とは、プログラミング言語の型システムの性質を表すもので、プログラミング言語の各要素(定数、変数、式、オブジェクト、関数、メソッドなど)についてそれらが複数の型に属することを許すという性質を指す。

むずかしいですね

ただ、本書をここまで読み進めていただいたあなたであれば、本書冒頭でお伝えした 「場合」を表現するためのひとつの方法 ということの意味がなんとなく理解できたかと思います

ifswitch と異なるのはその表現の仕方が非常に抽象的で、 「どのような処理が行われているのか」 を使用者に意識させないところにあります
どちらも「こういう場合にこうなる」を表現したいときに使えるのですが、 ifswitch は「こういう場合に」と「こうなる」の間の「どうやって」が見えちゃうんですね
「どうやって」の部分は冗長なので、隠蔽しているのがポリモーフィズム だと思っていただければ、概ね差し支えないです

ポリモーフィズムを効かせることの何が嬉しいのか

結局重要なのはこれだと思います
メリットがわからないと使おうという気にならないですよね

無駄に読ませないための術

まずひとつ目は 「読まなくていい部分を読ませない」 ことです
2 章の ガード節 を紹介したところでも出てきましたね

8 章の最後のコードをもう一度ご確認ください

const weaponType = WEAPON_TYPE.BOW;
const weapon: Weapon = createWeaponInstance(weaponType)

weapon.attack()
const weaponName = weapon.name
console.log(weaponName)

具体的な実装を確認しなくても「武器で攻撃するのか〜」とか「武器の名前が取れるのか〜」というのはこのコードだけで読み取れると思います
「実際にどう処理しているの?」が気になる人だけがその先の具体的な処理を覗きに行くでしょう

バグの調査だったり、機能追加のための調査だったり、 ソフトウェアエンジニアの実務はコードを書く時間よりもコードを読む時間のほうが大きい ことに注意してください

そんなときに ここは関係ないから読み飛ばそう とすぐに判断できることは、開発スピードを上げる大きな助けになってくれます

拡張しやすい設計

「太刀」の場合であっても「弓」の場合であっても、「武器」を使うことに変わりはないです
例えばこれから 「片手剣」が追加されたとしても、「ヘビィボウガン」が追加されたとしても、「武器」を使うことに変わりはない ですよね

仕様が追加(拡張)されたとしても、「武器」を使う側のコードに変更は入りません

  1. Weapon を実装した SwordAndShield classHeavyBowgun class を定義して
  2. WEAPON_TYPE に新しい定数追加して
  3. createWeaponInstance の中を修正する

これだけで、 Weapon使っている側のコードには一切手を加えることなく 仕様追加ができてしまいます
これは素晴らしいことです

「すでに動いているコード」というのはたくさんの人に 触られていてバグが見つけられていて直されている はずのコードなので、その 洗礼されたコードを仕様の追加で壊したくはない ですからね

局所化される改修箇所

もうひとつのメリットとしては、 バグの修正や機能の改変の対象を局所化 させることができ、 改修による影響を閉じ込めることができる ことです

どういうことかというと、例えば下記 2 件のようなタスクがあったとします

  • 「弓の攻撃の処理」にバグが見つかったので修正する
  • 「太刀の攻撃の処理」をより強力に調整する

8 章のコードのようにそれぞれが class で分かれていれば、 お互いのタスクは競合することなくそれぞれで実装を行い、テストを行い、マージする ことができます
もちろん使っている側のコードにも影響がないので、 局所的に修正を完結させる ことができます

しかし、 4 章のコードのようにどちらのタスクでも同じ関数内を修正しないといけない場合はどうでしょう
お互いの対応が競合していないかを確認しながら慎重に進め、テストでも他の箇所に影響が広がっていないか注意して行わなければならなくなります

結果として、 無駄なコストがかかってしまう わけですね

このような「拡張は広く受け入れて、修正はその一部だけで済ませてね」みたいな考え方を 開放閉鎖原則 なんて言ったりするので、気になったら Google 先生 に聞いてみてください


ここまででひとまず初級編が終わりです

どうでしょう、本書の目標だった「ポリモーフィズムを使ったコードが書けるになる」「ポリモーフィズムが使われているコードが読めるようになる」は達成できましたでしょうか

試しに宿題です
身近なもの何でも良いので interface と具象 class の関係になるものを探してみてください
どういった特徴を持っていてどのような振る舞いをするのか、 普段からそのように物事を抽象的に捉えるトレーニングをする と、より使いこなせるようになっていくでしょう

さて、次章からはより発展的な内容やポリモーフィズムを使った実践的な内容を解説していきます

  • 連想配列を利用した createWeaponInstance から switch を無くす方法
  • constructor injection を使った依存性の注入
  • ラムダ式を用いた具象クラスの簡略化と、それを活かした Weapon 型が attack を持つことの不自然さの解決
  • 継承というもうひとつのアプローチと移譲
  • ストラテジーパターン、テンプレートメソッドパターン、ステートパターンを中心に GoF デザインパターンを紹介する
  • 「あるかもしれないし、ないかもしれない」 maybe というアプローチとメソッドチェーン

こんな内容を書いていきたいんですけど、後半とかもうその話題で一冊余裕でかけちゃうくらいの文量になりそうな気がするので、ここから先は不定期連載形式にしていこうと思います

そして、内容はどんどん難しくなっていくと思います
内容にわからないところがあっても自分を責めないでください、正直「 GoF デザインパターン」とかぼくもソフトウェアエンジニア歴 4 年目とかでやっと理解できた内容だったので、、、
(もちろんデキる方は数ヶ月とかで習得しちゃうと思います!)

もしもわからないことがあれば、その場合は気兼ねなくぼくの Twitter までお願いします
ひとに何かを教えることは自分自身の成長にも繋がるので、一緒に優秀なソフトウェアエンジニアを目指していきましょう

それではひとまず休憩して、準備ができましたら、次章からの脱初学者の扉を開いてみてください