Java
Android
リファクタリング

[読書メモ]リファクタリング - タイプコードの置き換え(振る舞いに影響を与えるタイプコードの場合)- 1

(What)振る舞いに影響を与えるタイプコードとは?

例えば、こんな感じ。

振る舞いに影響を与えるタイプコード.java
public int getPayAmount() {
    switch (getType()) {
        case ENGINEER:
            return getMonthlySalary();
        case SALESMAN:
            return getMonthlySalary() + getCommission();
        case MANAGER:
            return getMonthlySalary() + getBonus();
        default:
            return 0;
    }
}

タイプコードを判定し、それの結果によって異なるコードを実行している箇所です。
だいたいif - elseswitch文が今回題材とする箇所に該当します。

こういうコードを見たらやりたいことは↓の通りです。
1. タイプコードをポリモーフィックな振る舞いを実現する継承構造で置き換える
2. ポリモーフィズムで条件分岐を置き換える

(Why)なぜ置き換えるのか?

タイプコードの判定結果によってそれぞれ異なる処理をしている箇所を置き換えようとしているわけですが、これの利点は↓

★異なる条件での振る舞いについての知識をクライアントから取り除くことができる。
    => その結果、新たなバリエーションを追加する時には、バリエーションの追加だけで対応できるようになる。
    => ポリモーフィズムがないと、現在ある条件分岐全てを探して、それぞれ処理を追加しないといけない。
        => これは、追加漏れが発生する可能性を示唆し、不具合発生の温床となる

(How)どう置き換えるのか?

サブクラスによる置き換え

変更前.java
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case REQUEST_CODE_FOR_EVALUATION_SEND:
                       // 何かの処理
            break;
        case REQUEST_CODE_FOR_SMS_SEND_ACTIVITY:
                       // 何かの処理
            break;
        case REQUEST_CODE_FOR_CAMERA_USAGE:
                       // 何かの処理
            break;
        default:
    }
}
変更後.java
class ActivityResult {
    public static final int REQUEST_CODE_FOR_EVALUATION_SEND = 1;
    public static final int REQUEST_CODE_FOR_SMS_SEND_ACTIVITY = 2;
    public static final int REQUEST_CODE_FOR_CAMERA_USAGE = 3;

    static ActivityResult create(int requestCode) {
        switch (requestCode) {
            case REQUEST_CODE_FOR_EVALUATION_SEND:
                return new ActivityResultEvaluationSend();
            case REQUEST_CODE_FOR_SMS_SEND_ACTIVITY:
                return new ActivityResultSmsSend();
            .. // 他のコードについても同様
        }
    }

    private ActivityResult() {}

    abstract int getRequestCode();
}

class ActivityResultEvaluationSend extends ActivityResult {
    @Override
    public int getRequestCode() {
        return ActivityResult.REQUEST_CODE_FOR_EVALUATION_SEND;
    }
}

class ActivityResultSmsSend extends ActivityResult {
    @Override
    public int getRequestCode() {
        return ActivityResult.REQUEST_CODE_FOR_SMS_SEND_ACTIVITY;
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    ActivityResult result = ActivityResult.create(requestCode);
    // @todo resultに処理を実装する。
}

これで、サブクラスによる置き換えができました。
これはやりたかったことの最終形ではなく、この後にポリモーフィズムになったので各サブクラスにそれぞれの処理を実装することになります。
しかし、それは別で書こうと思います。