ゲームが作れるようになるまでがんばる日記

ゲーム制作のことを中心にゲームに関することを書いています

タイプコードをクラスで置き換える

オブジェクトの種類を表すタイプコードにintのような基本型で表現することはよく行う。しかしそれでは型チェックがうまく効かないことがある。

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;
    private final String name;

    public Item(int itemtype, String name) {
        this.itemtype = itemtype;
        this.name = name;
    }
}
public class Main {
    public static void main(String[] args) {
        Item weapon = new Item(Item.ITEMTYPE_WEAPON, "SWORD");
        Item armor  = new Item(Item.ITEMTYPE_ARMOR, "PLATE");
        Item shield = new Item(Item.ITEMTYPE_SHIELD, "WOOD");
    }
}

このようにアイテムの種類をint型で表現した場合、範囲外の値を入れてしまったり、あるいは敵の種類を表すタイプコードを誤って入れてしまってもコンパイル時には気づくことができない。
そこで、このタイプコードをクラスで置き換えて、コンパイル時に型チェックが行われるようにする。

手順

・タイプコードを表す新しいクラスを作る
互換性のため基本型を使ったインタフェースも用意
・今までのタイプコードを新しいクラスを使うようにする
このとき基本型を使った古いインタフェースを使う

コンパイルして確認する。

・基本型を使わない新しいインタフェースを作成
・古いインタフェースを新しいインタフェースに置き換える

置き換えるたびにコンパイルして確認。

・古いインタフェースを削除

コンパイルして確認。


上の例でリファクタリングした結果は次のようになる。リファクタリングの途中経過は省略。

// アイテムの種類のタイプコードを表すItemTypeクラス
public class ItemType {
    public static final ItemType WEAPON = new ItemType(0);
    public static final ItemType ARMOR = new ItemType(1);
    public static final ItemType SHILED = new ItemType(2);

    private final int typecode;

    private ItemType(int typecode) {
        this.typecode = typecode;
    }

    public int getTypecode() { return typecode; }
}

このクラスのコンストラクタをprivateにすることによって、外部から作成できないようにしておくのがポイント。

public class Item {
    private final ItemType itemtype;
    private final String name;

    public Item(ItemType itemtype, String name) {
        this.itemtype = itemtype;
        this.name = name;
    }
}
public class Main {
    public static void main(String[] args) {
        Item weapon = new Item(ItemType.WEAPON, "SWORD");
        Item armor  = new Item(ItemType.ARMOR, "PLATE");
        Item shield = new Item(ItemType.SHIELD, "WOOD");
    }
}

もし、識別さえできればよく、typecodeの数字が必要なかったら、typecodeフィールドを削除することができる。
J2SE5.0からはenumを使ってより簡単に定義できる。

public enum ItemType {
    WEAPON(0),
    ARMOR(1),
    SHIELD(2);

    private final int typecode;

    private ItemType(int typecode) {
        this.typecode = typecode;
    }

    public int getTypecode() { return typecode; }
}

コンパイル時に型チェックが行われるのはよいが、クラスが増えてしまうのがちょっと気になる。場合によっては基本型で表現するのもありだろう。


参考文献:『Java言語で学ぶリファクタリング入門』