ポリモーフィズムのサンプルコード
twitterで@happy_ryo(id:happy_ryo)とポリモーフィズムについて話していたので,簡単なサンプルを書いてみました.(実装クラスは非常にくだらない内容ですがあくまでサンプルということで^^;)
本題はインターフェイスと実装クラスの分離ができ,実行時にアプリケーションの状況により動的に実装をインターフェイスにバインドできるというのがわかればOKだと思います.
具体的には,animal.sanpo();と呼び出している部分,Dog, Catのインスタンスにかかわらずに散歩させることができます.これをポリモーフィズムを使わずに書くとコンパイル時に決定する処理になうので,ifやswitchを使いまくるはめになると思います.ポリモーフィズムを使えば実装クラスを追加するのも容易となります.
ただ,ポリモーフィズムを必要とするかどうかわからないのに,このようなクラス構成を闇雲に実装してしまうのはよろしくないので,YGNIで必要に応じて実装していくというのが正しい使い方だと思います.
abstract class
本質は,インターフェイスと実装ですがabstractはどこで使うの?って質問を受けたことがありましたので,それも使い方の一例を書いておきました.abstract classには,各実装クラス間で利用するような抽象的な,つまり共通的な機能を実装するパターンがもっとも多いと思います.骨格実装とかいったりします。私の場合,ポリモーフィズムを実装しなければならない場合は,インターフェイス→抽象クラス→実装クラスのクラス階層を基本的には作っています.
package sample; /** * 動物インターフェイスです. * * @author junichi * */ public interface Animal { /** * 動物が鳴きます. */ public void cry(); /** * 動物が走ります. */ public void run(); /** * 動物が座ります. */ public void sit(); /** * 動物が散歩します. */ public void sanpo(); }
package sample; /** * 動物の抽象クラスです. * <p> * 抽象クラスはこうやって使う. 名前のごとく抽象的に表現できる機能しか実装しない. * </p> * * @author junichi * */ public abstract class AbstractAnimal implements Animal { /* * (非 Javadoc) * * @see sample.Animal#cry() */ public abstract void cry(); /* * (非 Javadoc) * * @see sample.Animal#run() */ public abstract void run(); /* * (非 Javadoc) * * @see sample.Animal#sit() */ public abstract void sit(); /** * 散歩メソッド * <p> * このメソッドは,テンプレートメソッドパターンで実装しています. cry, run, * sitは,実行時にバインドされているインスタンスのメソッドが呼ばれます. その名のとおりひな形的なメソッドです. * </p> */ public void sanpo() { this.run(); this.cry(); this.sit(); } }
package sample; /** * 動物を生成するファクトリインターフェイスです. * * @author junichi * */ public interface AnimalFactory { /** * 引数に応じた動物を生成します. * * @param animalWord * 動物生成に使う文字列 * @return 動物 */ public Animal create(String animalWord); }
package sample.impl; import sample.AbstractAnimal; /** * 猫を実装したクラスです. * * @author junichi * */ public class Cat extends AbstractAnimal { /* * (非 Javadoc) * * @see sample.AbstractAnimal#cry() */ public void cry() { System.out.println("にゃんにゃん!"); } /* * (非 Javadoc) * * @see sample.AbstractAnimal#run() */ public void run() { System.out.println("にゃんにゃんが走る"); } /* * (非 Javadoc) * * @see sample.AbstractAnimal#sit() */ public void sit() { System.out.println("にゃんにゃんが座る"); } }
package sample.impl; import sample.AbstractAnimal; /** * 犬を実装したクラスです. * * @author junichi * */ public class Dog extends AbstractAnimal { /* * (非 Javadoc) * * @see sample.AbstractAnimal#cry() */ public void cry() { System.out.println("わんわん!"); } /* * (非 Javadoc) * * @see sample.AbstractAnimal#run() */ public void run() { System.out.println("わんわんが走る"); } /* * (非 Javadoc) * * @see sample.AbstractAnimal#sit() */ public void sit() { System.out.println("わんわんが座る"); } }
package sample.impl; import sample.Animal; import sample.AnimalFactory; /** * 動物を生成するファクトリの実装クラスです. * * @author junichi * */ public class AnimalFactoryImpl implements AnimalFactory { public static final String CAT = "cat"; public static final String DOG = "dog"; /* * (非 Javadoc) * * @see sample.AnimalFactory#create(java.lang.String) */ public Animal create(String animalWord) { if (animalWord == null) { return null; } Animal animal = null; if (DOG.equals(animalWord.toLowerCase())) { animal = new Dog(); } else if (CAT.equals(animalWord.toLowerCase())) { animal = new Cat(); } return animal; } }
package sample.impl; import sample.Animal; import sample.AnimalFactory; /** * メインクラスです. * * @author junichi * */ public class Main { private AnimalFactory animalFactory; /** * コンストラクタです. * * @param animalFactory */ public Main(AnimalFactory animalFactory) { this.animalFactory = animalFactory; } /** * 開始メソッドです. * <p> * ここでは,動物ファクトリで動物を生成し,動物に散歩させています. * </p> * * @param args */ private void start(String[] args) { Animal animal = animalFactory.create(args[0]); animal.sanpo(); } /** * メインメソッドです. * <p> * Mainクラスに動物ファクトリの実装クラスのインスタンスを渡して,開始します. * </p> * * @param args */ public static void main(String[] args) { new Main(new AnimalFactoryImpl()).start(args); } }