到達点は、実装を取り替えられる境界を作ることです。前提は第05回です。
呼ぶ側が必要とする約束
通知処理で必要なのは「メッセージを送れること」です。画面表示かメールかという実装の詳細まで、業務処理が知る必要はありません。インターフェースに操作の約束を置き、実装を外から渡します。これを依存性の注入と呼びます。大きなフレームワークは不要です。
public class Main {
interface Notifier {
void send(String message);
}
static final class ConsoleNotifier implements Notifier {
public void send(String message) {
System.out.println("通知: " + message);
}
}
static final class StudyService {
private final Notifier notifier;
StudyService(Notifier notifier) {
this.notifier = java.util.Objects.requireNonNull(notifier);
}
void complete(String lesson) {
notifier.send(lesson + "を完了しました");
}
}
public static void main(String[] args) {
StudyService service = new StudyService(new ConsoleNotifier());
service.complete("クラスの学習");
new StudyService(message -> System.out.println("テスト記録: " + message))
.complete("設計の学習");
}
}
Main.javaで実行すると通常の通知とテスト記録が一つずつ表示されます。StudyServiceはNotifierを「持つ」関係です。これが合成です。実装が異なっても同じ約束で呼び出せる性質を、多態性と呼びます。
継承を選ぶ基準
継承は単なるコードの使い回し機能として乱用しないようにします。親型として使う利用者の期待を、子型も守れるかを考えます。例えば「必ず保存できる保存先」を継承して「常に保存禁止の保存先」を作ると、期待を壊す可能性があります。
変えたい部分を小さな部品として持つ方が、影響範囲を小さくできる場合があります。ただし、すべてのクラスに機械的にインターフェースを作る必要もありません。外部通信、保存先、時計など、変更やテスト差し替えの理由がある境界から始めます。
練習と解答
練習:画面に出さず、通知をリストへ記録する実装を作ります。何を検証すべきでしょうか。
解答例:List<String> messages = new ArrayList<>(); を用意し、new StudyService(messages::add) として注入します。完了後の要素数が1で、内容が期待した文章であることを確認します。privateな内部処理ではなく、通知という外部から見える結果を確かめます。
公式資料
インターフェースを参照できます。
Java全20回の目次 | 前の回 | 次の回