コードを使わずストーリー形式でオブジェクト指向を説明してみる
オブジェクト指向についての分かりにくい説明
オブジェクト = 「モノ」と訳され、ある約割を持ったモノごとにクラスを分けて、クラス同士の関係性を定義していくことで、システムを作り上げようとする考え方のこと
みたいな解説を多く見かけますが、プログラミング初心者からしたらこの説明は抽象的すぎて分かりにくい...
結論、オブジェクト指向とは、
「変更と拡張に備えることを目的としたコードの書き方(考え方)」
だと考えれば、なんとなく理解しやすいかと思います。
ただ、これだけだとやっぱり分かりにくいので、プログラミング初心者(自分も含む)が分かるように、コードを一切書かずに、ストーリーを使って説明してみようと思います。
Aくんとロボット
登場人物とあらすじ
Aくんとロボット
Aくんはとても面倒くさがり屋さんです。
そんなAくんはどうしても、カレーライスが食べたくなりました。
でも自分で作るのは面倒なので、ロボットに料理を作ってもらうことにしました。
このロボットは、自分で考えて動くことはできません。
命令されたとおり忠実に動作するように作られています。
材料とレシピ
食材 | 道具 |
---|---|
じゃがいも | 包丁 |
たまねぎ | ピーラー |
にんじん | 鍋 |
牛肉 | コンロ |
カレールー | お玉 |
水 |
カレーライスを作成
Aくんは以下のような手順でロボットに命令しました。
- 「じゃがいも」、「にんじん」の皮を「ピーラー」でむく
- 「たまねぎ」は手で皮をむく
- 「包丁」で「じゃがいも」「にんじん」は「乱切り」、たまねぎは「くし切り」にする
- 「牛肉」、「たまねぎ」、「じゃがいも」、「にんじん」を「鍋」に入れ、「コンロ」に火をつけ炒める
- 「鍋」に「水」を加え沸騰させる
- 「カレールー」を入れ、「お玉」でかき混ぜながら15分煮込む
これで無事に、カレーライスが完成しました。めでたしめでたし。
今日もカレーが食べたい...
またカレーが食べたくなったAくんは、再びロボットに命令をしていきます。
しかしAくんは、また同じ命令をするのは面倒だと思いました。
そこであるものを持ってきました。
このカードには、ロボットにさせたい行動を記述することができます。
そしてこのカードをロボットに読み込ませることで、記録した行動を実行させることができるようになります。
こうして、前回同様の手順
- 「じゃがいも」、「にんじん」の皮を「ピーラー」でむく
- 「たまねぎ」は手で皮をむく
- 「包丁」で「じゃがいも」「にんじん」は「乱切り」、たまねぎは「くし切り」にする
- 「牛肉」、「たまねぎ」、「じゃがいも」、「にんじん」を「鍋」に入れ、「コンロ」に火をつけ炒める
- 「鍋」に「水」を加え沸騰させる
- 「カレールー」を入れ、「お玉」でかき混ぜながら15分煮込む
これらをカードに記述し、ロボットに読み込ませました。
すると、材料と道具をロボットに渡しただけで、カレーライスが完成しました。
Aくんはカレーライスを何度でも簡単に作らせることができるようになったのです。
シチューが食べたい
今度は「ホワイトシチュー」を作らせることにしました。
作り方はカレーライスとほぼ同じで、ルーの種類を変えれば良いだけなので、カレーライスのカードが再利用できると考えたのです。
また、お肉の種類も牛肉ではなく、鶏肉に変更することにしました。
そうして、カレーライスのときと同様、カードをロボットに読み込ませ、
- 「牛肉」→「鶏肉」
- 「カレールー」→「ホワイトシチューのルー」
に変更し、あとは同じ材料をロボットに渡し、作らせたのです。
問題発生
そうすると、鶏肉を前にして、ロボットがフリーズし、動かなくなってしまいました。
これは、カードには「牛肉」と記述されているのに「鶏肉」を渡してしまったことが原因のようです。
ロボットには、カードに記載されたことを忠実に再現することしかできないのです。
つまり、このカードでは、カレーライス以外を作ることができません。
穴あきカード
そこで、Aくんはカードに記述する内容を変更することにしました。
- 「〇〇」の皮を「〇〇」でむく
- 「〇〇」で「〇〇」を「〇〇」にする
- 「〇〇」を「〇〇」で炒める
- 「〇〇」に水を加え沸騰させる
- 「〇〇」を入れ、「◯」分煮込む
このように具体的な部分は空けておき、その都度材料を渡せば、処理してもらえると考えたのです。
Aくんの予想通り、空白にしておいたところは、直接渡した材料、道具を使って処理をしていくれるようになりました。
このカードを「煮込み料理カード」と名付けました。
今後は、渡す材料を変更するだけで、「ハヤシライス」「ビーフストロガノフ」「肉じゃが」など煮込み料理なら何でも作れるようになったのです。
カードの分割
ここまできたらAくんはさらに改良を加えようとします。
現在、「煮込み料理」しか作ることができませんが、途中の「皮をむく」動作や、「包丁で切る」動作、「炒める」動作などを、別々のカードに分けることで、さらにいろんな料理に応用できるのでは?と考えたのです。
つまり、「煮込み料理」としてカードを作るのではなく、分解して、
- 「皮をむくカード」
- 「カットするカード」
- 「炒めるカード」
- 「煮込むカード」
というふうに分割してカードを作成することで、例えば「煮込むカード」以外を使えば、炒めものを作ることができるようになります。
カードを整理
こうしてどんどんカードを分割して作成していくと、どこにどのカードがあるのか管理をすることが大変になってきました。
そこで、
「材料の下処理をするカード入れ」
「調理をするカード入れ」
を作り、整理して管理することにしました。
こうすることで、材料に対して、何か下処理をしたいときには、「材料の下処理に関するカード入れ」の中から、「皮をむくカード」や「カットするカード」などを探せば良いので、カードの管理がしやすくなりました。
抽象カード
- あいまいなカード
- カードの分割と整理
これらを行った結果、さらにこんなことができるようになりました。
加熱をする料理カードを作成しました。
ここには「〇〇を〇〇に入れ〇〇する」とだけ書かれています。
# 煮込み料理
「カットするカード」で材料をカット
「鍋」に入れ
「煮込むカード」を使う
# 炒めもの
「カットするカード」で材料をカット
「フライパン」に入れ
「炒めるカード」を使う
といった具合に、抽象的な処理が書かれたカードに、具体的な処理が書かれたカードを組み合わせることによって、様々な料理が作れるようになったのです。
Aくんがやったことを整理
Aくんがやってきたことは、全てオブジェクト指向に則った行動でした。
Aくんがやったこと | オブジェクト指向 |
---|---|
穴あきカード | メソッドの引数 |
カードの分割 | 単一責任 |
カードを整理 | メソッドのクラス化 |
抽象カード | 抽象クラスと継承 |
穴あきカード → メソッドの引数
処理を具体的に書くのではなく、こちらが必要な材料( = データ)を渡すことで、同じカードを使い回せるというメリットがありました。
カードの分割 → 単一責任
複数の処理を繋げて書いてしまうと、後々使い勝手が悪くなったため、Aくんはカードを分割しました。そうすると、別のカードを作るときに、再利用できるようになりました。
もしカードに書く内容を間違ってしまった場合でも、分割しておくことで、どこが間違っているのか特定するのも容易になります。
このように一つのカードが一つの処理にのみ責任を負っていることを「単一責任」と言います。
カードを整理 → 複数メソッドのクラス化
関連する処理は一つの箱( = クラス)にまとめた方が管理がしやすくなります。
抽象カード → 抽象クラスと継承
穴あきカードと同様の考え方を、さらに大きな概念として考えています。具体的なカードを作るのではなく、抽象的な内容しか書かれていないカードを作っておき、具体的な処理は、具体的な処理が書かれたカードに任せることで、さまざまな変更(煮込み料理の他に炒めものを作りたい)に対応することができるようになっていました。
このように、抽象的なカードを基に、具体的なカードを作っていくことを「継承」と言います。
オブジェクト指向のまとめ
序文でも述べましたが、オブジェクト指向とは、「変更と拡張に備える」ことを目的とした、上記のような考え方を総称したものというのが答えとなります。
つまり、オブジェクト指向を一言で説明しきれないのが、理解を難しくしている原因でもあるのかなと思います。
とにかく、オブジェクト指向(の考え方?)で開発をしていくことでコードが整理されていき、さらに変更や拡張にも対応しやすくなるというメリットがあるため、よく使われるのです。
長々と最後まで読んで頂き、ありがとうございました。
さらにこちらの記事で、オブジェクト指向において重要な「3大要素」を解説しています。
興味がありましたら、ぜひご一読ください。
Discussion