図1 操作対象となる3つの箱<BR>ふたが載せてあるもの,鍵がついているもの,リボンが付いているものの3つがある。
図1 操作対象となる3つの箱<BR>ふたが載せてあるもの,鍵がついているもの,リボンが付いているものの3つがある。
[画像のクリックで拡大表示]
図2 例題プログラム(その1)
図2 例題プログラム(その1)
[画像のクリックで拡大表示]
図3 図2にあるbox_open手続きの内容
図3 図2にあるbox_open手続きの内容
[画像のクリックで拡大表示]
図4 図2のプログラムを改良したもの&lt;BR&gt;パラメータの呼び出し方が変わっている。
図4 図2のプログラムを改良したもの<BR>パラメータの呼び出し方が変わっている。
[画像のクリックで拡大表示]
図5 メソッドの定義
図5 メソッドの定義
[画像のクリックで拡大表示]

ポリモーフィズム

 ポリモーフィズム(Polymorphism)とは,複数を意味する接頭語「poly-」と形態を意味する「morph」との合成に,接尾語「-ism」が付いたものです。つまり「複数の形態であること」という意味です。和訳して「多態」と呼ばれることもあります。

 別の言い方をすれば,多態とは複数の種類のものをあたかも同じものであるかのように扱うことができることを言います。

 これだけだと,なんのことだか分からないかもしれないので,一つのたとえを使いましょう。

 ここに3つの箱があります。箱にはそれぞれふたがあり,ふたが乗せてあるだけのもの,鍵が付いたもの,リボンで結んであるものに分かれます。箱自体が非常に高価なので,それぞれの箱には専任のオペレータが付いていて,箱に対してなにかしたいときには,オペレータに命令します(図1[拡大表示])。

 3種類の箱はそれぞれ開け方が違っています。しかし「ふたを開けろ」と命令すれば,それぞれの箱の開け方を知っている専任のオペレータが命令を実行してくれます。ですから,種別が違うこれら3種類の箱を同じように「箱であって,ふたを開けることができるもの」として取り扱うことができます。これこそがポリモーフィズムの本質です。

 プログラミングでは「箱を開けろ」という命令のことを「メッセージ」,それぞれの種別に応じた具体的な箱の開け方を「メソッド」と呼びます。

実際のプログラム

 上の例題を表現するプログラムを図2[拡大表示]に示します(別掲記事「今回使ったRubyの文法」を参照)。

 box_openという手続きが上の例題における「専任のオペレータ」だと考えてください。box_openを呼び出すと,パラメータ(かっこの中の値)の箱の種類に応じたやり方で箱を開けます。ただし,ここでは「ふたを開けます」と表示することで,実際に開けたとしています。この「種別に従って適切な処理を進める」ことがポリモーフィズムの基本です。

 しかし,図2だけでは十分とはいえません。box_openという手続きを自分で定義することを考えてみましょう。単純にこの手続きを実現しようと思うと,図3[拡大表示]のようになるでしょう。

 しかし,図3のコードはあまりうれしくありません。新しく箱の種別が増えるたびにbox_open手続きを書き直す必要がありますし,box_openのほかにも箱の種別に応じてポリモーフィズムが適用される手続きがたくさんあったりしたら,プログラムをあちこち修正しなければならなくなり,種別の追加はげっそりするほどの手間になってしまうでしょう。

 修正個所が増えれば増えるほど間違いが入り込む危険性が高まりますし,結果として正しく動かないプログラムができ上がってしまう可能性が増えます。

 このような修正を人間が直接こなすべきではありません。データの種別に応じて適切な処理(メソッド)を選ぶ動作は,道具であるプログラミング言語が対応するべきです。そのような支援があってこそ十分なポリモーフィズムと呼べるでしょう。

 さて,そのような十分なポリモーフィズムの働きを見るための下準備として図2のプログラムを変形してみましょう(図4[拡大表示])。

 図4のプログラムでは,パラメータが先頭に移動して「.」が追加されています。このコードを「先頭の式の値に対してopenというメッセージを送る」と解釈します。つまり,「先頭の式の値の種類に応じたopenという手続きを呼び出す」というように振る舞います。よりポリモーフィズムを意識した呼び出し方になっています。

 図4のプログラム中のそれぞれに応じた処理であるメソッドの定義は図5のようにしました。

 図5[拡大表示]のプログラムでは,3種類の箱,box1,box2,box3のそれぞれに直接「どうやって開くか」という方法を教えています。

 図5のプログラムと図3のプログラムを比べてみると,明示的な条件判断が消えて,すっきりしているのが分かります。しかも,ただ簡潔なだけではありません。図5のプログラムでは新しい箱の種別,例えば「スライドさせて開ける箱」が導入されたとしても,既存のプログラムに一切手を入れる必要がありません。手を入れる必要がないということは,間違いを仕込んでしまう危険性もないということです。

ポリモーフィズムのありがたさ

 ポリモーフィズムについて説明しましたが,ポリモーフィズムがあると何がうれしいのでしょう。

 まず第1にいろいろな種類のデータを統一的に扱うことができるので,プログラムがHow(どのように処理するか)ではなく,What(なにを行いたいか)に集中できます。そのため,プログラムの意図が細かな処理に埋没せず,読みやすく簡潔なプログラムを実現できます。

 第2に対象となるデータに応じて自動的に最適な処理が選択されますから,プログラム内部での不整合が起きません。鍵付きの箱をリボン付きの箱を開ける手続きに渡しておかしなことが起きることを心配しなくても済みます。誤って間違いを組み込んでしまわないことは,プログラムの開発において大変重要です。プログラマの負荷を減らすことにもつながります。

 第3に新しいデータへの対応を簡単に追加できますから,プログラムの拡張性が高まります。

 このように,ポリモーフィズムはプログラム開発の効率を高めてくれるのです。オブジェクト指向プログラミングを構成するテクニックのうち,ポリモーフィズムは最も重要なものと言えます。

*            *            *

 来月はRubyを使ってオブジェクト指向を構成する残りのテクニックであるデータ抽象と継承について学びます。