1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Java】実務で急に登場して焦った「レジストリパターン」を完全に理解する

1
Posted at

実務の設計レビューやコードを読んでいるとき、「レジストリパターン(Registry Pattern)」というに出てきて、「名前は聞いたことあるけど、具体的にどう実装してどう使うんだっけ…?」となったことはありませんか?
シングルトンやFactoryパターンに比べると少し影が薄い印象がありますが、特定のユースケースでは今でも非常に強力な武器になります。
今回は、Javaを使った具体的な実装例を交えながら、誰でもわかるようにレジストリパターンの基本と使いどころを解説します!

1. レジストリパターンとは?

一言でいうと、「オブジェクト(インスタンス)を共通の場所に登録(レジストリ)しておき、必要なときに名前などをキーにして取り出せるようにするパターン」です。
身近な例で例えるなら、「ホテルのクローク(荷物預かり所)」です。

荷物を預ける(登録:register)
引き換え券(キー:key)をもらう
必要なときに引き換え券を出して荷物を受け取る(取得:get)

シングルトンパターンとの違い

  • シングルトン: システム内に**特定のクラスのインスタンスが「絶対に1つ」**しか存在しないことを保証する。
  • レジストリ: 複数の異なるインスタンスを1つの管理クラス(レジストリ)にまとめて保持し、キーを使って出し分けられる。

2. Javaでのシンプルな実装例

「文字列のキー」を渡すと、それに応じた「通知処理(サービス)」を返すレジストリを実装してみます。

① インタフェースと実装クラスの準備

まずは、レジストリに登録したいオブジェクトの共通インタフェースを作ります。

public interface MessageService { 
    void sendMessage(String message);
}

// 実装A: Email送信
public class EmailService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Emailを送信: " + message);
    }
}

// 実装B: Slack送信
public class SlackService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Slack通知を送信: " + message);
    }
}

② レジストリクラスの実装

Javaの Map を使ってインスタンスを管理するのが一般的です。

import java.util.HashMap;
import java.util.Map;

public class MessageServiceRegistry {
    // インスタンスを保持するマップ
    private static final Map<String, MessageService> registry = new HashMap<>();

    // 登録用メソッド
    public static void register(String key, MessageService service) {
        registry.put(key, service);
    }

    // 取得用メソッド
    public static MessageService get(String key) {
        MessageService service = registry.get(key);
        if (service == null) {
            throw new IllegalArgumentException("未登録のキーです: " + key);
        }
        return service;
    }
}

③ 実際に使ってみる

アプリケーションの起動時などに登録を済ませ、業務ロジックではキーを指定して取得します。

public class Main {
    public static void main(String[] args) {
        // 1. 最初に対象のインスタンスを登録しておく
        MessageServiceRegistry.register("EMAIL", new EmailService());
        MessageServiceRegistry.register("SLACK", new SlackService());

        // 2. 動的にキーを受け取って処理を切り替える
        String userPreference = "SLACK"; // DBや画面からの入力など、動的に変わる値を想定

        // レジストリからインスタンスを取得
        MessageService service = MessageServiceRegistry.get(userPreference);

        // 実行(振る舞いが動的に切り替わる)
        service.sendMessage("こんにちは!"); // 「Slack通知を送信: こんにちは!」が出力される
    }
}

3. 実務での「使いどころ」は?(メリット)

実務において、このパターンがなぜ重宝されるのかというと、「大量の if-else や switch 文を撲滅できるから」です。
もしレジストリを使わない場合、以下のような条件分岐コードになりがちです。

// アンチパターン:分岐が増えるたびにこのメソッドを修正する必要がある
public MessageService getService(String type) {
    if ("EMAIL".equals(type)) {
        return new EmailService();
    } else if ("SLACK".equals(type)) {
        return new SlackService();
    }
    throw new IllegalArgumentException();
}

これだと、新しく「LINE通知」を追加したときに、この分岐ロジック自体を修正(変更)しなければならず、拡張に対してクローズ(OCP: 開放閉鎖原則の違反)になってしまいます。
レジストリパターンを使えば、新機能を追加する際は「新しいクラスを作って、最初の登録処理に register するだけ」で済むため、既存の業務ロジックに一切手を加える必要がなくなります。既存システムの置き換え(リプレイス)プロジェクトなど、保守性・拡張性が強く求められる現場で特によく見られます。

4. あれ? SpringのDIコンテナで良くない?

Java(特にSpring Bootなど)を触っていると、それ、DIコンテナがやってることでは?」と思うかもしれません。
結論から言うと、その通りです。
Spring Frameworkの ApplicationContext 自体が、巨大なレジストリパターンの実装体と言えます。

じゃあどうやって棲み分けるの?

現代の実務(特にSpring環境)では、手動で register をガリガリ書くのではなく、DIの「Map注入」機能と組み合わせてスマートにレジストリを自動構築することが多いです。

@Component
public class MessageServiceRegistry {

    // SpringがMessageServiceを実装した全Beanを自動でMapに注入してくれる!
    // クラス名(先頭小文字)や @Component("名前") で指定した文字列が自動的にキーになります
    @Autowired
    private Map<String, MessageService> registry;

    public MessageService get(String type) {
        MessageService service = registry.get(type);
        if (service == null) {
            throw new IllegalArgumentException("未登録のタイプです: " + type);
        }
        return service;
    }
}

手動での登録漏れが起きず、新しくクラスを追加するだけで自動認識されるため、実務のコードが劇的に綺麗になります。

まとめ

レジストリパターンは、オブジェクトのクローク(預かり所)
if-else の増殖を防ぎ、拡張性に強いコードが書ける。
現代のJava(Spring等)では、DIコンテナのMap注入機能と組み合わせることで真価を発揮する。
「急に会話に出てきて焦った」という方も、本質は「Mapでインスタンスを管理して出し分けるだけ」なので、ぜひ臆せず実務のコードリーディングや設計に活かしてみてください!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?