はじめに
Open/Closed原則を守った例外処理について考えてみました。
問題
新しい例外が増えるたびにcatch句やif文を追加して対応する
try {
// 処理
} catch (SqlException e) {
// ...
} catch (IOException e) {
// ...
} catch (BusinessLogicException e) {
// ...
} // 新しい例外が増えるたびに追加
解決策
Strategyパターンを使う
// 共通インターフェース
public interface ExceptionHandler {
boolean supports(Throwable cause);
void handle(Throwable cause);
}
// 具体ハンドラ
public class SqlExceptionHandler implements ExceptionHandler {
public boolean supports(Throwable cause) {
return cause instanceof SQLException;
}
public void handle(Throwable cause) {
System.out.println("DB error handled by SqlExceptionHandler");
}
}
// 具体ハンドラ
public class IoExceptionHandler implements ExceptionHandler {
public boolean supports(Throwable cause) {
return cause instanceof IOException;
}
public void handle(Throwable cause) {
System.out.println("IO error handled by IoExceptionHandler");
}
}
// 例外ハンドラDI
List<ExceptionHandler> handlers = List.of(
new SqlExceptionHandler(),
new IoExceptionHandler()
);
// 例外処理
try {
someService.doTask();
} catch (MyAppException e) {
Throwable cause = e.getCause();
handlers.stream()
.filter(h -> h.supports(cause))
.findFirst()
.ifPresentOrElse(
h -> h.handle(cause),
() -> System.out.println("Unhandled error: " + cause)
);
}
このようにしておけば、新たに例外を追加する際、
// 新しい例外ハンドラ追加
public class AnotherExceptionHandler implements ExceptionHandler {
public boolean supports(Throwable cause) {
return cause instanceof AnotherException;
}
public void handle(Throwable cause) {
System.out.println("Another error handled by AnotherExceptionHandler");
}
}
// 例外ハンドラDI
List<ExceptionHandler> handlers = List.of(
new SqlExceptionHandler(),
new IoExceptionHandler(),
new AnotherExceptionHandler() // <- 追加
);
を追加するだけで済みます。