3.0「ポリモーフィズム」というのは、オブジェクト指向プログラミング言語のもつ性質のひとつです。平たくいえば「見た目は同じ、中身は別人」ということです。家の中にはリモコンがいくつかあるでしょう。そのほとんどに「電源」という名前のボタンがあります。そのボタンを押せば電源が入ります。TVかビデオか、あるいはエアコンか、どの電化製品が動くかはリモコン次第です。このポリモーフィズムを活かすと、条件分岐の処理もすっきりと整えられることがあります。一般にはクラスの設計として採上げられるお題を、本稿の前半はフレームアクションで説明してみましょう。クラスについての考え方は、後半で解説します。インスタンスごとに条件分けして異なった動きを与えるタイムラインにMovieClipインスタンスを3つ置いて、それぞれに異なったアニメーションをさせてみましょう。インスタンスの動きは単純に、それぞれ3次元空間で水平と垂直に回し、2次元平面で伸び縮みさせます(図001)。ステージをクリックしたら、インスタンスすべてをもとの状態に戻します。図001■3つのインスタンスに3次元空間の水平・垂直回転と2次元平面の伸縮をさせる3つのMovieClipインスタンスには、それぞれmy0_mc〜my2_mcという名前をつけておきます(図002)。アニメーションは、DisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)のリスナー関数が扱います。ステージのクリックは、Stageオブジェクト(DisplayObject.stageプロパティ)のInteractiveObject.clickイベント(定数MouseEvent.CLICK)にリスナー関数を登録して受取ります。図002■タイムラインに置いた3つのMovieClipにインスタンス名をつけるまずは、インスタンスごとに条件で切り分けて、それぞれのアニメーションを制御してみます(スクリプト001)。3つのMovieClipインスタンスは、まとめて扱いやすいように配列に入れます(第2行目)。イベントDisplayObject.enterFrameとInteractiveObject.clickのリスナー関数(xMove()とxStop())は、ともにforステートメントで配列に納めたインスタンスをすべて取出し(第11行目〜および第32行目〜)、switchステートメントによりインスタンスを仕分けて処理します(第13行目〜および第34行目〜)[*1]。1;break;}}}ふたつのリスナー関数におけるswitchステートメントの処理内容は、とくに難しいところはありません。ひとつひとつのインスタンスの扱いは、ごく単純といえます。この調子で進めていくと、面倒なことになりそうな予感がします。アニメーション(xMove())と停止(xStop())のほかに、画像を切替えるとか、3つのインスタンスに対する別の操作がさらに加わるかもしれません。するとまた、forループで取出したインスタンスをswitchステートメントで切り分ける関数が増えます。異なるアニメーションをさせる新たなインスタンスが、出てくることも考えられます。そのときは、それぞれの関数に条件分岐をひとつひとつ書き加える必要があります。そこまでやり終えてから、ひとつのインスタンスの振舞いを変えることになったら、すべての修正箇所を探すのに頭が痛くなりそうです。第23回「クラスのデザインとループ処理」の「ループ処理で複数のインスタンスを生成する」および第14回「キー操作とif以外の条件判定」の「switchステートメント」をお読みください。インスタンスに同じ名前の関数を定義して呼出すそれでは、ポリモーフィズムの考え方を採入れてみましょう。前掲スクリプト001で、インスタンスのアニメーションや停止を、それぞれ関数(xMove()やxRotate())としてまとめることは正しいです。インスタンスの側から見ると、自分に対してどのような処理が行われるのかは、いくつもの関数の中身をひととおり眺めないとつかめません。そこで、インスタンスの処理は、そのシンボルのフレームアクションに関数としてまとめます。すると、今度はメインタイムラインからは、それぞれのインスタンスごとに別々の関数を呼出さなければなりません。ここで、ポリモーフィズムです。インスタンスそれぞれの異なった関数に、すべて同じ名前をつけてしまいます。アニメーションさせるのはxMove()、その停止はxStop()と、リスナー関数と同じ関数名にしましょう(スクリプト002)。これでリスナー関数は、インスタンスすべてに対して、同じ名前の関数を呼出せば済みます(第12および21行目)。mcs_array[i];my_mc.xStop();}}ふたつのリスナー関数xMove()とxStop()は、ともにforループで配列に納められたインスタンスすべてを取出し、それぞれに対してリスナー関数と同名の関数を呼出しています(スクリプト002第10〜13、および第19〜22行目)。条件判定がなくなって、すっきりとしました。あとはそれぞれのインスタンスのシンボルに、フレームアクションとしてリスナー関数から呼出される関数xMove()とxStop()を定義すればよいです(スクリプト003〜005)。前掲スクリプト001第1行目で宣言していた定数(DEGREES_TO_RADIANS)は、その値を用いるシンボルのフレームアクション(スクリプト005第1行目)に移しました。1;}これで、それぞれのインスタンスの動きは、そのシンボルのフレームアクションを見れば把握できます。あるインスタンスの振舞いを変えるのも難しくないでしょう。新たなアニメーションのインスタンスを加えるときは、そのフレームアクションに決まった関数(xMove()とxStop())を定義して、インスタンスの配列(mcs_array)に加えるだけで済みます。インターフェイスとクラスを定義する前掲スクリプト003〜005のフレームアクションをクラスとして定義し直します。[ライブラリ]に納められているmy0_mc〜my2_mcのシンボル(Clip0〜Clip2)には、クラスとしてClip0〜Clip2を設定しておきます(図003)。第22回「MovieClipシンボルにクラスを定義する」をお読みください。図003■[ライブラリ]のMovieClipシンボルにクラスを設定するたとえば、前掲スクリプト003をつぎのようなクラスClip0として定義すれば、前掲のメインタイムラインのフレームアクション(スクリプト002)で正しく動かすことができます[*2]。これではクラスを使うことのもち味が、十分に活かされていません。0;}}}3つのインスタンスmy0_mc〜my2_mcは、みな決まった関数をもち、フレームアクションから同じように扱えます。そのことをクラスとして明らかにしましょう。第1に、3つのクラスに決まった関数(xMove()とxStop()))が備わっていることを保証します。第2に、3つのインスタンスを同じデータ型として扱えるようにします。すると今回の例では、インスタンスを配列でなくVectorオブジェクトに納められます。場合によっては、インスタンスを処理する関数に引数として、同じデータ型で渡せます。した期待に応えるのが、インターフェイスです。インターフェイスは、それを実装するクラスが備えなければならないメソッドを宣言します。インスタンスはインターフェイスで型指定することができます。クラス(Clip0〜Clip2)が実装するインターフェイス(IClip)をつぎのように定義しました(スクリプト006)。xStop():void}}3つのクラス(Clip0〜Clip2)は、いずれもSpriteクラスを継承し、インターフェイスIClipを実装します(スクリプト007〜009。Spriteクラスの継承について後述注[*2]参照)。3つのクラスのインスタンスはインターフェイスIClipでデータ型を指定でき、実装されているふたつのメソッド(xMove()とxStop())が呼出せます。1;}}}前述スクリプト002のフレームアクションは、以下のスクリプト010のように書替えます。第1に、3つのインスタンスは配列でなく、IClipをベース型とするVectorオブジェクトに納めました(第1行目)。第2に、ふたつのリスナー関数のforループの処理で、Vectorオブジェクトから取出したインスタンスはIClip型の変数に取出して、実装されたメソッドを呼出しています(第11〜12行目および第20〜21行目)。instances[i];my_mc.xStop();}}ポリモーフィズムを使うことにより、条件の分岐処理はなくしてしまえました[*3]。大事な点はふたつです。第1に、インターフェイスを実装すると、異なるクラスが同じデータ型として扱え、しかも同じメソッドを備えさせられるということです。第2に、実際には違うクラスですから、実装したメソッドの処理はクラスに応じて定められます。これにより、異なるクラスのインスタンスを同じように扱いながら、クラスごとに処理が定められ、修正や拡張もしやすくなるのです。本文で例として示したクラスClip0は、MovieClipクラスを継承したので前掲スクリプト002のフレームアクションで「正しく動かす」ことができます。MovieClipはダイナミックなクラスなので、前掲スクリプト002第11〜12行目のようにMovieClip型で指定した変数に取出して、Clip0クラスに定義したメソッド(xMove)を呼出せるからです。このクラスClip0を以下のようにSpriteのサブクラスにすると、Spriteで型指定した変数に対してClip0クラスのメソッドを呼出せば、コンパイルエラーが起こります(図004)。0;}}}図004■インスタンスをSprite型で取出すとクラスのメソッドが呼出せないスクリプト006のインターフェイスIClipを実装することにより、3つのクラスClip0〜Clip1のインスタンスを同じIClip型で指定した変数に入れて、IClipに宣言したメソッドが呼出せるのです(前掲スクリプト010参照)。ポリモーフィズムについてWebでは、Javaなどのプログラミング言語で解説されることが多いようです。Exploited「ポリモーフィズムを使ったリファクタリングの実践例」、トルネード・プログラミング「ポリモーフィズムによる条件分岐の排除」などが参考になるでしょう。