🎉

プログラミングにおける「購読」と「発行」完全ガイド

に公開

🧠 はじめに

プログラミングをしているとよく出てくる「購読(subscribe)」や「発行(publish
/ emit)」という用語。
どちらも直訳すると少し分かりにくい言葉ですが、簡単に言えば次のような関係です:

  • 購読(subscribe):変化を「受け取る側」として登録する\
  • 発行(publish / emit):変化を「送る側」として通知する

この2つは、アプリケーションの「イベント駆動」「リアクティブプログラミング」「状態管理」といった場面で必ず登場します。


📡 購読(subscribe)とは?

「購読」とは、ある値やイベントが変化したときに通知を受け取るよう登録することです。

イメージしやすい例は「YouTubeチャンネルの購読」です。登録しておくと、新しい動画が出た時に通知が来ますよね。
プログラミングでもそれと同じで、「あるデータの変化」や「イベントの発生」を自動で受け取れるようになります。

✅ 例1:Riverpodでの購読

final timerState = ref.watch(timerViewModelProvider);

timerViewModelProvider
の状態が変化するたびに、このWidgetは再ビルドされます。
ここで watch() が「購読」です。


✅ 例2:Streamの購読

myStream.listen((data) {
  print('新しいデータ: $data');
});

listen()
はストリームを「購読」しており、新しい値が流れてきたら自動的に実行されます。


🚀 発行(publish / emit)とは?

一方、「発行」は購読と対になる概念で、新しい値・イベントを送り出すことです。

購読者が待っている「通知」を発信する側です。
「出版社(Publisher)」が本を出すと、それを購読している読者に届くようなイメージです。


✅ Riverpodでの発行

あなたのコードで言えば、TimerViewModel 内で以下のように state = ...
を更新している箇所がすべて「発行」です:

state = state.copyWith(status: TimerStatus.running);

この瞬間、購読している ref.watch(...)ref.listen(...)
に「新しい状態」が通知され、UIが更新されます。


✅ Streamでの発行

final controller = StreamController<int>();

// 発行(emit)
controller.add(42);

// 購読
controller.stream.listen((value) {
  print('受け取った: $value');
});

.add() は購読者に値を「発行」している行為です。


🪄 「購読」と「発行」はセットで考える


役割 説明 よく使われる関数


購読 値やイベントの通知を受け取るよう登録する watch(), listen(), addListener()
(subscribe)

発行 (publish 新しい値やイベントを送り出す state = ..., add(), emit()
/ emit)

この2つは必ずペアで存在します。購読者がいなければ通知は届かず、発行者がいなければ何も起きません。


🧰 よくある使い方まとめ

技術 / 機能 発行の方法 購読の方法


Riverpod state = ... ref.watch() / ref.listen()
Stream controller.add() stream.listen()
ValueNotifier value = ... addListener()
EventBus / PubSub publish() subscribe()


🎯 まとめ

  • 「購読」は「受け取る側の登録」\
  • 「発行」は「送る側の通知」\
  • 状態管理、リアクティブプログラミング、イベント駆動など、あらゆる場面で使われる基本概念\
  • Riverpodの場合、state = ... が「発行」、watch()listen()
    が「購読」

📌 一言で言うと:

購読は"聞く準備"であり、発行は"話しかけること"
この2つを正しく理解すると、イベント駆動や状態管理の仕組みが一気にわかりやすくなります。


🔕 解除(unsubscribe / cancel)とは?

購読と発行のペアを理解する上で、もう1つ重要な概念が「解除(unsubscribe)」です。
これは、「もう通知は受け取りたくない」と登録を解除することです。

✅ Streamでの購読解除

final subscription = myStream.listen((data) {
  print('受け取った: $data');
});

// 解除(購読をやめる)
subscription.cancel();

これで、myStream に新しいデータが発行されても通知は届かなくなります。


✅ Riverpodでは?

ref.watch()
は自動的にライフサイクルに基づいて購読が解除されるため、通常は手動で解除する必要はありません。
しかし、ref.listen() を使っている場合は、戻り値の
ProviderSubscription を利用して手動で close() できます。

final sub = ref.listen(timerViewModelProvider, (prev, next) {
  print('状態が変わった: $next');
});

// 解除
sub.close();

✅ 実世界の例え

  • 購読:ニュースレターを受け取る登録をする\
  • 発行:ニュースレターが送られてくる\
  • 解除:もう届かないように登録を解除する

✅ 3つまとめて整理


概念 説明 例


購読 (subscribe) 通知を受け取るよう登録する listen(), watch()

発行 (publish / emit) 通知を送り出す state = ..., add()

解除 (unsubscribe / 通知を受け取るのをやめる subscription.cancel(),
cancel)
close()

購読・発行・解除は、どれか1つだけでは機能しません。
「登録 → 通知 →
解除」というライフサイクルの3ステップで理解すると、リアクティブなコードが格段に読みやすくなります。

Discussion