はじめに
前回の続きです。前回の記事を見てない方はコチラから!
前回は「関数」を取り扱いましたが、今回はプログラミングにおける「オブジェクト指向」についてお話しようと思います。
これまでプログラミングを趣味でやったことがある人でも、
「オブジェクト指向って何?」
と思っている人もいると思います。
実際、「オブジェクト指向」という概念を知らなくてもプログラミングはできるので、知らない方がいてもおかしくはありません。
ただこの「オブジェクト指向」は、JAVAやC++など「複数人で1つのプログラムを作成することに向いている」プログラミング言語において、とても重要な概念となるので、知っておいて損はないでしょう。
今回はそんな「オブジェクト指向」を「仕事」に例えてお話してみようと思いますので、興味のある方はぜひご一読ください。
(個人的にプログラミングしていて思ったことを、つらつらと書いています。あくまでも例えなので、内容の正確性を保証できません。ご了承ください。)
仕事で例える「オブジェクト指向」
A社では修理できない
前回、あなたはA社から頼まれた「オーダーメイドで自動車を提供する」サービスを構築しましたね。
今回はそのサービスの利用者から、なにか問い合わせがあったそうです。
どんな問い合わせでしょうか? 利用者に話を聞いてみましょう。
「弊社サービスをご利用いただき誠にありがとうございます。今回はどのようなご用件でしょうか?」
「すみません、オタクの自動車が壊れたんですけど、修理サービスやってないんですか?」
なんと、修理のご依頼ですね!
前回は微塵も考えていなかったため、当然ながらA社には修理サービスなんかありません。
「申し訳ございません。弊社では修理のご依頼を承っておりません」
「はぁ」
あなたの対応に対し、お客様は明らかに落胆しております。
一体どうして落胆しているのでしょうか?(すっとぼけ)
サービスは「もの」に宿る
皆さんは、自分が購入した商品が壊れた際、どうしますか?
安い商品なら再購入すればいいかもしれませんが、高価であったり、通常のお店では売っていないようなものだったりすると、なかなかそうはいきませんよね。
たとえばお使いのスマートフォンに不具合が生じたとき、すぐに「スマートフォンを再購入しよう」とは考えないと思います。
まずはインターネットで検索したり友達に相談したりして、解決しなさそうだったら修理業者に依頼でしょうか?
こんな時にあったらいいなと思うのは、やっぱり「公式のカスタマーサポートサービス」ですよね。
商品に一番詳しいのは、もちろん製造したメーカー自身ですから。
もし「うちは特定のものを作るだけ」「うちは特定のものを修理するだけ」といったように、各会社が別々にサービスを展開していたら、
● A社の自動車をB社の「自動車修理サービス」に依頼したが、断られた
● C社の「自動車修理サービス」に依頼したところ、無事修理することができた
なんていう風に、利用者にとって不便極まりない世界ができてしまいますね。
製造したメーカーは責任をもって、最後までサービスを提供してほしいものです…。
そうです。「もの」は作るだけでなく、カスタマイズしたり、修理したりと様々な関連サービスが求められます。
この「サービスはものに宿る」という考え方こそが、「オブジェクト指向」、つまり「ものを中心に考える主義」なのです。
プログラミングにおけるオブジェクト指向
前回、プログラミングの「関数」のことを「サービス」だとお話したことを覚えていますか?
そして今回、オブジェクト指向とは「サービスはものに宿る」という考え方だとお話ししました。
では、プログラミングにおける「オブジェクト」とは、一体なんのことでしょうか?
答えは、
//「c++」や「java」
int i = 0;
float a = 33.4f;
//「python」
i = 57
m = "くぁwせdrftgyふじこlp"
これです。「変数」ですね。
つまり、プログラミングにおけるオブジェクト指向とは、「関数は変数に宿る」という考え方なのです。
オブジェクト指向のメリット①:型(クラス)を自由に作れる
前回お見せした、A社の(ほぼ業務委託している)自動車提供サービスのプログラムについて、JAVA風に書き直したものをお見せしましょう。
public class A{ // ここはいったん無視してください
// 返り値[自動車], サービス名[自動車提供], 引数[注文]
public static Car car_provide(Order order) {
// E社の設計サービスを引数[注文]で依頼し、[設計図]をもらう
Blueprint blueprint = E.design(order);
// B社の自動車部品調達サービスを引数[設計図]で依頼し、[自動車部品]をもらう
CarParts car_parts = B.car_parts_procure(blueprint);
// F社の組み立てサービスを引数[設計図]と[自動車部品]で依頼し、自動車をもらう
Car car = F.assembly(blueprint, car_parts);
// 依頼者に自動車を渡す
return car;
}
}
C言語を習っている人で、もしかしたらこう思った人もいるのではないでしょうか?
「あれ、BlueprintとかCarPartsなんて型あったっけ?」
そう、オブジェクト指向のまず1つめのメリットは、「型(クラス)を自由に作れる」というところです。
(C言語でも「構造体」という概念で同じことができますが…。)
自動車に必要な情報(サイズ、色、部品情報など)をまとめて一つの変数型(クラス)とすることで、「一つのまとまったデータ」として扱うことができます。
例えば、複数の自動車を用意する時、
// 1台目の自動車
int a_size = 10;
Sting a_color = "red";
// 2台目の自動車
int b_size = 15;
Sting b_color = "blue";
// 3台目の自動車
// :
// :
なんて1台1台の車にいちいちサイズと色の情報の変数を用意するのは面倒ですよね。
これが自動車クラスがあれば、
// 自動車クラスの定義
public class Car {
// 自動車が持つ変数(情報)
int size;
String color;
}
// 1台目の自動車
Car a = new Car(); // 新しい自動車を作ってくれるコマンド
a.size = 10;
a.color = "red";
// 2台目の自動車
Car b = new Car(); // 新しい自動車を作ってくれるコマンド
b.size = 15;
b.color = "blue";
// 3台目の自動車
// :
// :
という風に、一つの変数(インスタンス)のみでまとめることができます。
(行が長くなってるのは、サイズや色の指定を分かりやすくするためです。本当はもっと短く書けます)
「いや、何が嬉しいの?」
とまだこの便利さが伝わっていない人もいるかもしれません。
むしろ自動車クラスの定義でプログラムが長くなったと感じる人もいると思います。
では、この自動車クラスの神髄をお見せしましょう。
例えば、「自動車の装飾案を考えてくれるサービス(関数)」car_deco_planが用意されているとして、このcar_deco_plan関数に自動車の装飾案を考えてもらいます。
このcar_deco_plan関数は自動車のサイズと色の情報を元にいい感じに装飾案を考えてくれるので、サイズと色の2つの情報が必要です。
この時、自動車クラスがない場合とある場合で、どう変わるでしょうか?
// 自動車クラスがない場合
car_deco_plan(a_size, a_color);
car_deco_plan(b_size, b_color);
// 自動車クラスがある場合
car_deco_plan(a);
car_deco_plan(b);
自動車クラスがある場合の方が、圧倒的にすっきりしていますね!
今回は2つの変数で済んでいますが、もっと具体的な情報(車の各寸法、重量、各パーツごとの色)が必要になると、car_deco_plan関数に渡す情報が膨大になってしまいます…。
「クラスを自由に作れる」というメリット、お分かりいただけましたか?
オブジェクト指向のメリット②:関数に変数を渡さなくていい
オブジェクト指向のメリットはここからです。なんなら上のメリットはぜんぜん「オブジェクト指向」の「関数は変数に宿る」という考え方が反映されていません。
先程考えた「自動車の装飾案を考えてくれるサービス」ですが、上のコードではわざわざ引数として「自動車」を渡していますよね?
これはつまり、「他社にA社製の自動車を渡して、自動車の装飾案を考えてもらっている」ということなのです。
オブジェクト指向に基づけば、
「いや、公式サービスはよ」
です。
A社製のものを他社に渡して壊れたら元も子もないですし、信頼性や保障のことを考えると、A社製の製品ならそこに付随するサービスはA社で用意すべきですね。
もしA社が「装飾案を考えてくれるサービス」を展開していたら、プログラムは次のように書けます。
a.car_deco_plan();
b.car_deco_plan();
見ればわかるように、文字通り「関数が変数に宿って」いますよね?
これならA社製の自動車に付随するサービスですから、わざわざ自動車を引数として渡さなくてもきちんと装飾案を考えてくれるのです。
この「変数を渡さなくていい」というメリットの神髄をお見せしましょう。
「『装飾案を考えてくれる』じゃなくて、実際に装飾しろや!」
というそこのあなたに、いいお知らせがあります。
もし「作る」「装飾する」「修理する」サービスがすべて別会社だと、
// 注文票を用意します。お好きなように注文を作ってください
Order order = new Order();
// A社で作る
Car a = A.car_provide(order); // A社に注文を渡して自動車作ってもらい、受け取ります
// G社で装飾する
a = G.car_deco_plan(a); // G社にA社製の自動車を渡して装飾してもらい、返してもらいます
// H社で修理する
a = H.car_repair(a); // H社にA社製の自動車を渡して修理してもらい、返してもらいます
となります。
これでもすっきりはしていますが、もしA社が自動車をそのすべてのサービスを自社展開している素晴らしい会社なら、以下のような神コードが書けます。
// 注文票を用意します。お好きなように注文を作ってください
Order order = new Order();
// A社で作って装飾して修理する
Car a = A.car_provide(order).car_deco_plan().car_repair();
そう、わざわざ毎回利用者の下に自動車を返さずに、「作って」「装飾して」「修理して」くれるのです。(作った後になぜか修理しているのはともかく)
まるで「製品に処理を施している」かのように直感的に書けるのが、オブジェクト指向の素晴らしい所です。
「関数に変数を渡さなくていい」というメリット、お分かりいただけましたか?
他にもある…オブジェクト指向のメリット
今回はオブジェクト指向のメリットをほんの少ししか紹介できませんでした。
オブジェクト指向で重要な「カプセル化」「継承」「ポリモーフィズム」についてもお話しできませんでしたし…。
とにもかくにも、今回の記事で、オブジェクト指向のメリット、少しは伝わりましたでしょうか?
伝わってくれてると嬉しいです。
実はPythonでもクラスを定義して、オブジェクト指向でプログラミングができるので、興味が湧いた人はぜひ挑戦してみてください。
おわりに
今回、アドベントカレンダーの記事で「仕事で例えるプログラミング」シリーズを書いた動機は、「オブジェクト指向を自分なりに言語化したい!」という思いです。
予定では1本だけの投稿のつもりでしたが、いざ書いてみると内容が想像以上に膨れ上がってしまい、2本に分ける次第になりました。
前回記事からだいぶ日が空いてしまいましたが、アドベントカレンダーがもうすぐ終わってしまいそうなので、急いで書き上げた次第です。(クリスマスイブ前日にやることじゃない)
とりあえずイブには間に合いそうなので、何もなければ本記事は24日に上がっていると思います。
ということで、メリークリスマス!
(今年も残すことあと僅かですが、よいお年をお過ごしください)
…もうしばらく書かねー。