ポリモーフィズムとはなにか?超わかりやすく解説します!

最近は「オブジェクト指向」というキーワードをよく聞くようになりました。

これは、いままでのプログラミング手法と異なり、

モノが自主的に作用しあう形でプログラムを組むというようなプログラミング手法

などと言われます。

「こんな難しく言われてもわかんねえよ!」って方。私もそうでした。

こんにちは。今井@ima_maru)です。

私はこの概念を理解するのに一か月くらいはかかりました。

そのくらい理解が難しいものであるということは確かです。

大まかに言って、オブジェクト指向を構成する要素は3つあります。

  1. 継承
  2. カプセル化
  3. ポリモーフィズム(今回解説)

これら3つはオブジェクト指向の基盤を担っているものです。
オブジェクト指向を理解するならば避けては通れない道でしょう。

今回はその中でも一番難しいとされる「ポリモーフィズム」について解説します。

全く知らない人は本当に理解するのに時間がかかるかもしれません。

なので皆さんにより効率的に理解してほしいという想いから初めてプログラミングを学ぶ方にも優しい言葉で、できるだけわかりやすく解説していこうと思います。

この記事で解説するものは以下のようなことになります。

  • ポリモーフィズムってなに?
  • どんなことができるようになるのか。
  • メリットは?

オブジェクト指向の3つの特徴の1つのポリモーフィズムというのはどういうものなのでしょうか。見ていきましょう!

ポリモーフィズム(polymorphism

ポリモーフィズムとは、オブジェクトなどのデータ型に関する操作が統一的であることである。

なんて言われますが、正直こんな難しいこと言われてわかる人は少ないと思います。

もっともっと優しい言葉で解説しようと思います。

名前の由来

ポリモーフィズムとは、日本語で「多様性」「多態性」「多相性」と訳すことができます。読み方は、ポリモフィズムのほかに、ポリモフィズムとも読めるそうです。[r]の発音の問題ですね。

単語の構造的には poly morphism 分けられます。

poly(ポリ)には「多くの」という意味があります。

morphism(モーフィズム)は、簡単に「変化」といえばいいのかなと思います。あるものからあるものへの「変化」を表しています。

以上より、ポリモーフィズム(polymorphism)とは、「多くの」「変化」と直訳することができますね。「多くの変化が可能になる」という意味で「多様性」という言葉が当てはまるのだと思います。

Point

poly(多くの)morphism(変化)

多様性

ポリモーフィズムは実は簡単な意味

ポリモーフィズムという単語自体の意味は以外にもわかりやすかったりします。難しく考えずにこう考えてください。

「モノ」が「そのモノ」らしく振舞うこと

たとえば、アヒルはアヒルのように「クワッ!」っと鳴きます。当たり前ですね。
まあ、アヒルが犬のように「ワンッ!」っと鳴いたらおかしいですからね。

ポリモーフィズムとはこのぐらい当たり前のことを言っているんです。

プログラミングでいえば、「呼び出した関数」と「呼び出し元」の関係を言っています。

呼び出した関数は呼び出し元オブジェクトに適した振る舞いをする

犬と猫とアヒルを模したクラスを作り、それぞれに鳴くという関数を作成しておきます。

ポリモーフィズム
クラス 犬    { 鳴く() {  "ワン"  } };
クラス 猫    { 鳴く() { "ニャー" } };
クラス アヒル { 鳴く() { "クワッ" } };

それぞれの鳴き声はこんな感じで表してみます。

○.鳴く()を考えると、○に適した鳴き声を出すべきですよね。

  • 犬.鳴く() = “ワン”
  • 猫.鳴く() = “ニャー”
  • アヒル.鳴く() = “クワッ”

この普通に考えたら当たり前のこのことがポリモーフィズムだったりするんです。

猫は猫のように鳴き、犬は犬のように鳴く、呼び出し元に適したふるまいをするわけです。

もっと言えば「呼び出す関数の振る舞いは、呼び出したオブジェクトに適したものである。」と言い換えることもできますね。

ポリモーフィズム
犬.鳴く();
猫.鳴く();
アヒル.鳴く();

ここで考えるわけです。

「鳴くっていう動作はみんな共通して持っている。ならば、呼び出す動物を変えてまとめたほうがきれいになるのでは?」

ポリモーフィズムを使えば、このような処理ができるようになります。

for文で回すことを考えてみましょう。

ポリモーフィズム
動物 = { 犬,猫,アヒル };
for (動物) { 動物.鳴く(); }

こんな感じでしょうか?

動物に犬、猫、アヒルが順に入って、鳴き声という関数を呼び出します。

こう書けば、犬は「ワン」、猫は「ニャー」、アヒルは「クワッ」と鳴くはずですね。

動物が増えた時でも対応できるので、変更に強くなり何より見やすくなりました。

これはポリモーフィズムがもたらす結晶のようなもので、オブジェクト指向のプログラミングするうえで大切なものとなります。

今用いたのは私がテキトーに書いたコード(疑似言語)で、これを実際のプログラミング言語でできるようにしたのがポリモーフィズムです。

オブジェクト指向プログラミングではクラスやインスタンスのことを「オブジェクト」と呼び、犬やアヒルなどを一つの「モノ」として扱うようになります。

この「モノ」をまとめて操作できるというのがポリモーフィズムのメリットです。

これを一般的に難しい言葉で「異なるオブジェクト間の操作を一緒に処理できるというオブジェクト指向の特徴の一部」とか冒頭で述べたような難しい言い方になるわけです。

Point

オブジェクトなどのデータ型に関する操作統一的である

異なるオブジェクト間の操作
一緒に処理できる

異なるモノの操作まとめてできる

なんとなくでイメージをつかもう

ここでは導入として漠然としたイメージを書きます。

○○の髪型は□□だ。

このような日本語の構文があったとしましょう。これの○○に田中君や木村君を入れてみると、

田中君髪形リーゼントだ。
木村君髪形マッシュだ。

このようになったとします。
プログラミング的に言えば、大事な要素は以下の3つです。

  1. 呼び出し元
  2. 関数
  3. 結果

すべて、
「呼び出し元」の「関数」は「結果」
という構文になっています。

田中君髪形リーゼントだ。
木村君髪形マッシュだ。

呼び出し元が変われば髪型という関数が結果を返します。この際、髪型という関数名はいつも同じです。「同じ関数なのに呼び出した人によって結果が変わってくる。」というのがポリモーフィズムの考え方です。

 

ポリモーフィズムのメリット

ざっくりと概要をお話ししたところで、

  • ポリモーフィズムを用いるとどのようなメリットがあるのか?

というところについて解説していきます。

ポリモーフィズムのないプログラミングに比べて以下の点で優れているといえます。

  1. コードがきれいにまとまる
  2. プログラムの変更に強くなる柔軟性

二つのメリットは根幹をみれば、どちらも似たりよったりな部分です。

それぞれ解説していきます。

コードがきれいにまとまる

ポリモーフィズムを利用すれば、とてもコードがきれいにまとまります。

これは実際にコードを見てもらったほうがわかりやすいので、プログラミング言語「C++」を使って、

  • ポリモーフィズムの使わない例
  • ポリモーフィズムのうまく使った例

を紹介します。詳しいコードはそれぞれの言語での解説に回そうと思いますので、一部大事な部分だけを抜粋します。

今回使用するコードは、各国のあいさつを模した関数をそれぞれのクラスに作成し、それを呼び出すという一連のコードの呼び出し部分だけを抜粋したものです。

以下で紹介する2タイプのコードはどちらも同じ意味です。

それではまずは、ポリモーフィズムなしの場合。

ポリモーフィズム
//それぞれでHello()関数を呼び出す
american.Hello();
japanese.Hello();
indian.Hello();
korean.Hello();
chinese.Hello();
russian.Hello();
german.Hello();

「ちょっとカッコ悪い!」

つぎに、ポリモーフィズムありの場合。

ポリモーフィズム
//それぞれのオブジェクトのポインタを格納する配列を用意
Human* human[N] = 
{ &american,&japanese,&indian,&korean,&chinese,&russian,&german };

//それぞれでHello()関数を呼び出す	
for (int i = 0; i < N; i++) {
	human[i]->Hello();
}
「これはかっこいい。きれい。美しい。」

いかにも下のコードのほうができる人っぽいですよね?

またこれは、変更に強くなる柔軟性も兼ね備えているのです。

変更に強くなる柔軟性

先ほどのコードを再利用します。

ポリモーフィズム
//それぞれのオブジェクトのポインタを格納する配列を用意
Human* human[N] = 
{ &american,&japanese,&indian,&korean,&chinese,&russian,&german };

//それぞれでHello()関数を呼び出す	
for (int i = 0; i < N; i++) {
	human[i]->Hello();
}

ポリモーフィズムのない場合はこれ↓です。

ポリモーフィズム
//それぞれでHello()関数を呼び出す
american.Hello();
japanese.Hello();
indian.Hello();
korean.Hello();
chinese.Hello();
russian.Hello();
german.Hello();
  • このような処理をもう一度したいとき、同じコードをもう一度書く必要がある。
  • 単純にコードがキモチワルイ。

かっこ悪いし、こんな長々と書きたくないです。またこのような処理がしたいとき、またこのコードを書かないといけなくなるというのが、よくない点でしょう。

例えば、もう一回この関数を呼び出すときに、

ポリモーフィズム
//ポリモーフィズムあり
//それぞれでHello()関数を呼び出す	
for (int i = 0; i < N; i++) {
	human[i]->Hello();
}

こう書くのと、

ポリモーフィズム
//ポリモーフィズムなし
//それぞれでHello()関数を呼び出す
american.Hello();
japanese.Hello();
indian.Hello();
korean.Hello();
chinese.Hello();
russian.Hello();
german.Hello();

どっちがいいですかって話です。クラスが増えたら増えただけ、ポリモーフィズムのない下のコードのほうが大変になります。

次にすべてのクラスに新たなメンバ関数Hoge()が増えた時を考えてみましょう。

単純に考えてください。以下の二つのコードどちらがいいと思いますか?

ポリモーフィズム
//ポリモーフィズムなし

//それぞれでHello()関数を呼び出す
american.Hello();
japanese.Hello();
indian.Hello();
korean.Hello();
chinese.Hello();
russian.Hello();
german.Hello();

//それぞれでHoge()関数を呼び出す
american.Hoge();
japanese.Hoge();
indian.Hoge();
korean.Hoge();
chinese.Hoge();
russian.Hoge();
german.Hoge();
ポリモーフィズム
//ポリモーフィズムあり

//それぞれでHello(),Hoge()関数を呼び出す	
for (int i = 0; i < N; i++) {
	human[i]->Hello();
	human[i]->Hoge();
}

このように記述量が減るというのも変更に強い点ですが、さらには、変更点や追加点がわかりやすいという点も変更に強い点なのです。

大規模開発の際は、あとから変更する場面が多々あるので、変更点がわかりやすいのはとても大きなメリットです。

最後に

理解や解釈が難しいポリモーフィズムですが、私も最初理解に苦しみましたので、いろんな記事を見るとか、直接触れてみて感じてみるとかするととてもいいと思います。

またメリットなどは個人的な見解も含んでいますので、実際にプログラミングしてみてここは違うんじゃないと思うこともあるかもしれません。一つ参考程度に見てください。

この記事はプログラミングを学び始める方向けに作っていますので、これからプログラミングを始める方に少しでも役に立てれば、参考にしてもらえればと思います。

オブジェクト指向のメリットとは?例に例えてわかりやすく解説!

最後まで読んでいただきありがとうございます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です