この例で言う「処理」とは、「車に乗って目的地に行く」というものです。
ここで考えなくてはならないのは、その処理の具体的な手順ですが、これはフェラーリでも
BMWでも似たようなものですよね。2つとも「車」ですし、車を運転するときは、
「エンジンをかけて」、「目的地に向かい」、「エンジンを切る」という作業を決まって行います。
だけど、フェラーリとBMWではエンジンのかけ方が違うかもしれませんし、運転の仕方も、
エンジンの切り方も違うかも知れません。それでも大きく見れば似たような処理ですよね。
このような場合にはTemplate Methodパターンが有効です。
Template Methodでは処理の枠組み(フレームワーク)をスーパークラス側で定義し、
具体的な処理内容をサブクラス側で定義します。上の例だと「車に乗って目的地に行く」、
という処理はスーパークラスで記述します。そして、「エンジンをかける」、「目的地に向かう」、
「エンジンを切る」という処理をサブクラスで記述します。
ここで必要なクラスを洗い出して見ましょう。
DrivingCar |
「車に乗って目的地に行く」という処理を表す抽象クラス |
DrivingFerrari |
「フェラーリに乗って目的地に行く」という処理を表すクラス |
DrivingBmw |
「BMWに乗って目的地に行く」という処理を表すクラス |
具体的な実装方法としては、スーパークラス側の処理の枠組みを定義したメソッドは、
内部的な別のメソッドを呼び出すことで実装されます。そしてその内部的なメソッドを
抽象メソッドとし、サブクラス側で実装させます。こうすることで、スーパークラス側は
サブクラスでの具体的な処理を知らなくても、「車に乗って目的地に行く」ことが出来ます。
また、このプログラムのメインクラスを見てもらえれば分かるように、DrivingFerrariと
DrivingBmwのインスタンスはそれぞれ、DrivingCar型の変数、ferrariとbmwに代入されています。
ferrariとbmwには、厳密には違うインスタンスが入っているのに、driveメソッドの呼び出しにも
適切に応答しています。
これは一見すると奇妙ですが、当然のことです。何故ならDrivingFerrariと
DrivingBmwは同じDrivingCarクラスを継承していて、driveメソッドはDrivingCarで定義されているからです。
今回のプログラムはごく小規模なものですから簡単ですが、これがもっと大きなものになると、
同じスーパークラスを継承したサブクラスでも、instanceofなどを使ってどのクラスなのか
参照しなければならないことが多くなるかも知れません。けれども、そのような処理をしなければならないと
いうことは、概ね設計上に問題があります。「スーパークラス型の変数にはそのサブクラスであればどんな
インスタンスを入れても正しく動作する」、という原則のことをLSPと言いますが、
これはデザインパターンに留まらず、継承の大原則です。
適切な実装を行えば、instanceofを使う必要がない場合がほとんどです。
実際、instanceofを用いるコードは、多態性(ポリモーフィズム)を利用することで同様の処理が実現可能です。
多態性というのは、まさにこのTemplate Methodを適用したようなコードに見られます。
|