導入
こんにちは。けちょんです。
皆さん、保守性の低いコードを作ってしまっていませんか?
私が以前指摘され、改善したコードを紹介します。
改善前
コード
public class Main {
public static void main(String[] args) throws Exception {
if (args[0].equals(ExecPattern.A.toString())) {
exeA(args[1]);
} else if (args[0].equals(ExecPattern.B.toString())) {
exeB(args[1]);
} else {
throw new Exception("illigal request parameter");
}
}
private static void exeA(String input) {
// something
}
private static void exeB(String input) {
// something
}
}
public enum ExecPattern {
A, B
}
内容
実行時に渡した第1引数を用いて、IF文で処理を分岐させています。
実際にはロジッククラスを用意して、ロジック部分は外出ししていました。
何が悪いか
この場合、新たに実行パターンCが追加される際に新たにIF文を追加する必要があります。
保守性が低いですし、コントローラクラスが頻繁に更新されることはあまり好ましいことではありません。
追加による修正が最小限になることが理想です。
改善後
コード
abstract class ExecLogic {
abstract void execute(String args);
}
Logicクラスが継承する抽象クラス。
今回でいうと、AクラスやBクラスが継承します。
public class A extends ExecLogic {
@Override
void execute(String args) {
// something
}
}
public class B extends ExecLogic {
@Override
void execute(String args) {
// something
}
}
A,BクラスにはLogicを記載します。
public class Main {
public static void main(String[] args) throws Exception {
ExecLogic logic = (ExecLogic) Class.forName(args[0]).getDeclaredConstructor().newInstance(); //※1
logic.execute(args[1]);
}
}
Mainクラスでは、実行時引数からA,Bクラスのインスタンスを生成して、処理を実行しています。
※1:実行時引数から、A,Bクラスのインスタンスを生成しています。
余談ですが、Class.forName("className").newInstance()
はJava9からdeprecatedされています。
Class.forName("className").getDeclaredConstructor().newInstance()
を使用しましょう。
参考:https://qiita.com/deaf_tadashi/items/3c3118e660861fb43434
何が改善されたか
実際にCクラスを追加することを考えましょう。
修正内容は、Cクラスを追加して、中にロジックを書くだけです。
コントローラクラスには一切修正が不要になっています。
これで、追加時に修正を最小限にすることを達成しました。
おまけ
実際には、実行時引数をバリデーションする処理が必要になると思います。
そこで修正箇所が増えるのも避ける必要があります。
以下のような実装にしましょう。
private static boolean validate(String[] args) {
boolean res = false;
for (ExecPattern execPattern : ExecPattern.values()) {
if (execPattern.toString().equals(args[0])) {
res = true;
}
}
return res;
}