前回はタイプコードをクラスで置き換えたが、今回はサブクラスで置き換える方法。タイプコード別に動作が異なるようならサブクラスで置き換える。
// アイテムクラス public class Item { public static final int ITEMTYPE_WEAPON = 0; public static final int ITEMTYPE_ARMOR = 1; public static final int ITEMTYPE_SHIELD = 2; private final int itemtype; public Item(int itemtype) { this.itemtype = itemtype; } public void use() { switch(itemtype) { case ITEMTYPE_WEAPON: System.out.println("use weapon"); break; case ITEMTYPE_ARMOR: System.out.println("use armor"); break; case ITEMTYPE_SHIELD: System.out.println("use shield"); break; default: ; } } }
// テストメイン public class Main { public static void main(String[] args) { Item weapon = new Item(Item.ITEMTYPE_WEAPON); Item armor = new Item(Item.ITEMTYPE_ARMOR); Item shield = new Item(Item.ITEMTYPE_SHIELD); weapon.use(); armor.use(); shield.use(); } }
useメソッド内でswitch文でアイテムの種類別に動作を変えている。このswitch文をなくしてサブクラスへと移動する。
手順
- ・タイプコードを表すフィールドを隠して、getterメソッドで見せるようにする
- ・タイプコードを元にインスタンスを作成しているなら、Factory Methodにする
- ・タイプコードそれぞれの値ごとにサブクラスを作成
- タイプコードのgetterメソッドをオーバーライド。switch文での動作を移動
コンパイルしてテスト。
- ・タイプコードのフィールドを削除
- ・元クラスのメソッドのうち、サブクラスで実装するメソッドはabstractにする
コンパイルして確認。
上の例をリファクタリングした結果。途中経過は省略。
// アイテム抽象クラス public abstract class Item { public static final int ITEMTYPE_WEAPON = 0; public static final int ITEMTYPE_ARMOR = 1; public static final int ITEMTYPE_SHIELD = 2; public static Item createItem(int itemtype) { switch(itemtype) { case ITEMTYPE_WEAPON: return new ItemWeapon(); case ITEMTYPE_ARMOR: return new ItemArmor(); case ITEMTYPE_SHIELD: return new ItemShield(); default: throw new IllegalArgumentException("itemtype = " + itemtype); } } public abstract void use(); }
createItemメソッドでタイプコードごとのサブクラスのインスタンスを作成している。あるいは各サブクラスごとのcreateメソッドを用意する方法もある。
タイプコードのgetterはこの例では使用しないのでばっさりカット。
実際に作成されるインスタンスは各サブクラスなので、abstractを付ける。useメソッドもabstractを付ける。
// タイプコードごとのサブクラス public class ItemWeapon extends Item { @Override public void use() { System.out.println("use weapon"); } } public class ItemArmor extends Item { @Override public void use() { System.out.println("use armor"); } } public class ItemShield extends Item { @Override public void use() { System.out.println("use shield"); } }
前のクラスでswitch文で書かれたuseメソッドをそれぞれのuseメソッドへと移動。
// テストメイン public class Main { public static void main(String[] args) { Item weapon = Item.createItem(Item.ITEMTYPE_WEAPON); Item armor = Item.createItem(Item.ITEMTYPE_ARMOR); Item shield = Item.createItem(Item.ITEMTYPE_SHIELD); weapon.use(); armor.use(); shield.use(); } }
使用する側はcreateItemメソッドでインスタンスを作成する。