Javaを以前仕事で使っていた時に、分かりにくい処理があったのでそれをリファクタリングしたお話です。
リファクタリング前
リファクタリング前はこのようになってました。
private void doProcess() {
A a = initial();
if (!chkfnc1(a)) {
throw new XXXException(x1);
}
if (!chkfnc2(a)) {
throw new XXXException(x2);
}
if (!chkfnc3(a)) {
throw new XXXException(x3);
}
execute(a); //チェック後の実行処理
}
private boolean chkfnc1(a) {}
private boolean chkfnc2(a) {}
private boolean chkfnc3(a) {}
インスタンス取得 -> インスタンスの正統性チェック(n回) -> 実行処理
という流れになってます。
chkfncN
はシーケンスで流れるもので、順序の依存関係があるとします。
このようなままだとチェック要件が一つ増えるとchkfncN
関数がどのタイミングでチェックされるものか判断しづらくなり嫌気が差してきます。
Enum
Enumを聞くと、
- 数値と対応関係がとれる
- switch-caseで場合分けができる
等を思い浮かべると思います。
僕の中で思う最大な特徴は順序が保証されていることだと考えています。
Haskellの型クラスEnum の定義の一部にも
class Enum a where
succ :: a -> a -- 次の値を示す関数
pred :: a -> a -- 前の値を示す関数
が定義されています。
またJavaのEnumでも
for (DaysOfWeekEnum day : DaysOfWeekEnum.values()) {
}
このように順番に処理することができます
JavaのEnumにある機能
JavaのEnumはEnumの中に関数が定義できます
https://qiita.com/KeithYokoma/items/9681b130ea132cfad64d
組み合わせる
以上を踏まえてこのようにできます。
public enum MyChecker {
CHK1 {
@Override
public boolean chkfnc(A a) {}
@Override
public String erroMsg(){}
},
CHK2 {
@Override
public boolean chkfnc(A a) {}
@Override
public String erroMsg(){}
};
CHK3 {
@Override
public boolean chkfnc(A a) {}
@Override
public String erroMsg(){}
};
public abstract boolean chkfnc(A a);
public abstract String errorMsg;
}
先ほどの例を用いると
private void doProcess() {
A a = initial();
for (MyChecker val : MyChecker.values()) {
if (!val.chefnc) {
throw new XXXException(val.erroMsg);
}
}
}
とdoProcessの中もスッキリして見渡しが良くなりました。
またEnumを使うことで順序が保証されているので、どのような動作を保証したいかも一目同然になります。
まとめ
JavaでEnumのvaliesをこのように使っている記事があまりなかったので、書きました。
使える状況が限られますが、enumが順序を保証したものだと認識すれば他の応用も効くかと思います。