1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GoFデザインパターン】Observer(オブザーバー)パターンをJavaで理解する

1
Last updated at Posted at 2026-09-28

はじめに

本記事は、GoFデザインパターン解説シリーズの1つです。

今回は、振る舞いに関するパターンの中でも特に有名なObserver(オブザーバー)パターンを解説します。

以前に解説したリアクティブプログラミングの根底にある考え方でもあります。

解決したい課題

「あるオブジェクトの状態が変化したときに、それに関心を持つ複数の別オブジェクトに通知したい」という場面があります。

例えば、YouTubeのチャンネル登録を考えてみます。

「チャンネル(配信者)」が新しい動画を投稿したら、それを「登録している視聴者全員」に通知したいとします。

しかし、配信者側のクラスが「視聴者Aに通知する」「視聴者Bに通知する」のように、具体的な視聴者クラスを直接知って呼び出す実装をしてしまうと、視聴者の数が増えるたびに配信者側のコードを修正する必要が出てきますし、視聴者の登録・解除を動的に行うことも困難になります。

Observerパターンは、「通知する側(Subject)」が「通知を受け取る側(Observer)」の具体的な型を知らずに、共通インターフェース経由で一括通知できる仕組みを提供することで、この問題を解決します。

image.png

クラス構成

  • Subject(サブジェクト): 状態を持ち、変化があったらObserverに通知する側。Observerの登録・解除・通知の仕組みを持つ
  • Observer(オブザーバー): 通知を受け取るための共通インターフェース
  • ConcreteSubject(具体的なサブジェクト): 実際に状態を持つ、具体的なSubjectの実装
  • ConcreteObserver(具体的なオブザーバー): 通知を受けて実際に処理を行う、具体的なObserverの実装

image.png

Javaでの実装例

YouTubeチャンネルの例で実装してみます。

Observer:通知を受け取る共通インターフェース

public interface Subscriber {
    void update(String channelName, String videoTitle);
}

Subject:通知する側の共通インターフェース

public interface Publisher {
    void subscribe(Subscriber subscriber);
    void unsubscribe(Subscriber subscriber);
    void notifySubscribers(String videoTitle);
}

ConcreteSubject:実際の配信者クラス

import java.util.ArrayList;
import java.util.List;

public class YouTubeChannel implements Publisher {
    private final String channelName;
    private final List<Subscriber> subscribers = new ArrayList<>();

    public YouTubeChannel(String channelName) {
        this.channelName = channelName;
    }

    @Override
    public void subscribe(Subscriber subscriber) {
        subscribers.add(subscriber);
        System.out.println("[" + channelName + "] 新しい登録者が増えました");
    }

    @Override
    public void unsubscribe(Subscriber subscriber) {
        subscribers.remove(subscriber);
        System.out.println("[" + channelName + "] 登録が解除されました");
    }

    @Override
    public void notifySubscribers(String videoTitle) {
        // 登録している視聴者「全員」に、順番に通知する。個々の視聴者の型は意識していない
        for (Subscriber subscriber : subscribers) {
            subscriber.update(channelName, videoTitle);
        }
    }

    public void uploadVideo(String videoTitle) {
        System.out.println("[" + channelName + "] 新しい動画「" + videoTitle + "」を投稿しました");
        notifySubscribers(videoTitle);
    }
}

ConcreteObserver:具体的な視聴者クラス群

public class EmailSubscriber implements Subscriber {
    private final String email;

    public EmailSubscriber(String email) {
        this.email = email;
    }

    @Override
    public void update(String channelName, String videoTitle) {
        System.out.println("  → [メール通知: " + email + "] " + channelName + "が「" + videoTitle + "」を投稿しました");
    }
}

public class AppNotificationSubscriber implements Subscriber {
    private final String userName;

    public AppNotificationSubscriber(String userName) {
        this.userName = userName;
    }

    @Override
    public void update(String channelName, String videoTitle) {
        System.out.println("  → [アプリ通知: " + userName + "] 新着動画: " + videoTitle);
    }
}

利用側のコード

public class Main {
    public static void main(String[] args) {
        YouTubeChannel channel = new YouTubeChannel("itstar-lab公式チャンネル");

        Subscriber alice = new EmailSubscriber("alice@example.com");
        Subscriber bob = new AppNotificationSubscriber("bob");

        channel.subscribe(alice);
        channel.subscribe(bob);

        channel.uploadVideo("GoFデザインパターン入門");

        channel.unsubscribe(alice); // aliceは登録解除

        channel.uploadVideo("Observerパターン徹底解説");
    }
}

実行結果:

[itstar-lab公式チャンネル] 新しい登録者が増えました
[itstar-lab公式チャンネル] 新しい登録者が増えました
[itstar-lab公式チャンネル] 新しい動画「GoFデザインパターン入門」を投稿しました
  → [メール通知: alice@example.com] itstar-lab公式チャンネルが「GoFデザインパターン入門」を投稿しました
  → [アプリ通知: bob] 新着動画: GoFデザインパターン入門
[itstar-lab公式チャンネル] 登録が解除されました
[itstar-lab公式チャンネル] 新しい動画「Observerパターン徹底解説」を投稿しました
  → [アプリ通知: bob] 新着動画: Observerパターン徹底解説

image.png

YouTubeChannel(Subject)は、EmailSubscriberやAppNotificationSubscriberという具体的なクラスを一切知りません。

Subscriberという共通インターフェースを実装したオブジェクトを、登録・解除・一括通知しているだけです。

新しい通知方法(例えばSlack通知)を追加したい場合も、新しいSubscriber実装クラスを1つ追加するだけで、YouTubeChannel側のコードは一切変更不要です。

alice(EmailSubscriber)を登録解除した後は、uploadVideo()を呼んでもaliceには通知が届いていない点にも注目してください。

Java標準ライブラリとの関係

Java 9で非推奨(deprecated)になったjava.util.Observer/Observableという、まさにこのパターンをそのまま実装したクラスがかつて標準ライブラリに存在していました。

現在は、より柔軟なPropertyChangeListener(JavaBeans)や、GUIフレームワークにおけるイベントリスナー(ActionListener等)、そして以前解説したRxJavaのObservable/Observerなど、Observerパターンの考え方を発展させた仕組みが広く使われています。

使用場面

  • GUIにおけるイベントリスナー(ボタンクリック、値の変更など)
  • SNSのフォロー・チャンネル登録機能のような、パブリッシャー・サブスクライバー型の通知
  • MVC(Model-View-Controller)アーキテクチャにおける、Modelの変化をViewに通知する仕組み
  • リアクティブプログラミング(RxJava、Project Reactor等)の基盤となる考え方

まとめ

項目 内容
解決する課題 あるオブジェクトの状態変化を、具体的な型を知らずに複数のオブジェクトへ通知したい
実現方法 通知を受け取る側(Observer)を共通インターフェースとして定義し、Subject側で登録・一括通知する
メリット 通知先の追加・削除が、Subject側のコードを変更せずに動的に行える
関連技術 GUIイベントリスナー、MVCのModel-View連携、リアクティブプログラミングの基盤

おすすめ書籍

GoFの23パターンをより深く学びたい方には、以下の書籍がおすすめです。
Java言語のサンプルコードとUMLを使い、初心者にもわかりやすく解説されています。

Java言語で学ぶデザインパターン入門 第3版 [ 結城 浩 ]

※本リンクはアフィリエイトリンクを含みます。

参考

1
2
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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?