なんでも引数付きコンストラクタでやろうとする例外のインタフェースは使いにくいですよね。
ってお話です。
前提
- java8以降
問題となる例外
例えば2つ独自フィールドを持つ例外を作る場合
@Getter
public class SampleException extends RuntimeException {
private String code;
private String id;
...
コンストラクタどんなものを用意するかって話になります。
-
code
が入るパターン -
id
が入るパターン -
message
が入るパターン -
cause
が入るパターン
これの組み合わせ...って考えてコンストラクタを作り始めるともう大変です。
さらにcode
とid
とmessage
は同じ型なのでそれぞれに対してそれしか設定しないコンストラクタを作ることはできません。
public SampleException(String message, String id, String code) {
super(message);
this.id = id;
this.message = message;
}
として入らないところにはNullを入れればいい。って思うかもしれませんがスマートじゃないです。
また、コンストラクタが増えてくると使う側が順番を気にしなきゃいけなくて大変です。(ちゃんとjavadocを書いていれば見れば分かりますが、いちいち見なければ分かりません。)
フィールド2つでこのレベルなので、フィールドが増えてったら悲惨です...
流れるようなインタフェースで解決
@Getter
public class SampleException extends RuntimeException {
private String code;
private String id;
private SampleException(String message, Throwable cause) {
super(message, cause);
}
public static class SampleExceptionThrower {
private String message;
private Throwable cause;
private String code;
private String id;
private SampleExceptionThrower() {
}
public static void call(Consumer<SampleExceptionThrower> consumer) {
SampleExceptionThrower builder = new SampleExceptionThrower();
consumer.accept(builder);
SampleException exception = new SampleException(builder.message, builder.cause);
exception.code = builder.code;
exception.id = builder.id;
throw exception;
}
public SampleExceptionThrower setMessage(String message) {
this.message = message;
return this;
}
public SampleExceptionThrower setCause(Throwable cause) {
this.cause = cause;
return this;
}
public SampleExceptionThrower setId(String id) {
this.id = id;
return this;
}
public SampleExceptionThrower setCode(String code) {
this.code = code;
return this;
}
}
}
Fluent Builderパターンです。こんな風に使えます。
SampleExceptionThrower.call(builder -> builder.setCause(e)
.setMessage("message").setCode("code"));
すごくすっきり!
あとがき
callっていう命名が気に食わないので誰か何かいい案をください...