observer-pattern について
通知を受けたオブザーバブルがイベントデータを購読者へ伝える、JavaScript のデザインパターンを解説します。疎結合なイベント駆動型コンポーネントを構築する開発者向けに、購読管理と性能面の留意点も扱います。
主な機能
- 01オブザーバーの登録と解除
- 02イベントデータを購読者へ通知
- 03コンポーネント間の疎結合
- 04非同期イベントデータの処理
活用シーン
- 01ユーザー操作をログとトーストへ通知
- 02ダウンロード完了をコンポーネントへ配信
- 03新着メッセージを購読者へ共有
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ patternsdev/ skills --skill observer-patternインストール後、Claude Code で/observer-patternを入力して読み込まれたか確認します。
詳細
オブザーバーパターン
目次
オブザーバーパターンでは、特定のオブジェクト(オブザーバー)を、オブザーバブルと呼ばれる別のオブジェクトに 登録 できます。イベントが発生するたびに、オブザーバブルはすべてのオブザーバーに通知します。
使用する場面
- アプリケーションの複数の部分に状態の変化やイベントを通知する必要がある場合に使用します
- コンポーネント間のイベント駆動型で非同期な通信の実装に役立ちます
使用しない場面
- 購読者の数が非常に多く、通知のパフォーマンスが重要になる場合
- 1 対 1 の通信で、より単純なコールバックや直接的な関数呼び出しで十分な場合
- 暗黙的なイベント連鎖によるデバッグの難しさが、疎結合にするメリットを上回る場合
使い方
subscribe、unsubscribe、notifyメソッドを備えた Observable クラスを作成します- 関心事をより適切に分離できるよう、オブザーバーとオブザーバブルの結合を疎に保ちます
- 複雑なロジックを伴う多数の購読者への通知では、パフォーマンスに注意します
- より高度なリアクティブプログラミングが必要な場合は、RxJS などのライブラリの使用を検討します
詳細
オブザーバブルオブジェクトには通常、次の3つの重要な要素があります。
observers:特定のイベントが発生するたびに通知を受け取るオブザーバーの配列ですsubscribe():オブザーバーのリストにオブザーバーを追加するメソッドですunsubscribe():オブザーバーのリストからオブザーバーを削除するメソッドですnotify():特定のイベントが発生するたびに、すべてのオブザーバーに通知するメソッドです
ES6 クラスを使ってオブザーバブルを作成しましょう。
class Observable {
constructor() {
this.observers = [];
}
subscribe(func) {
this.observers.push(func);
}
unsubscribe(func) {
this.observers = this.observers.filter((observer) => observer !== func);
}
notify(data) {
this.observers.forEach((observer) => observer(data));
}
}
これで、subscribe メソッドを使ってオブザーバーのリストにオブザーバーを追加し、unsubscribe メソッドを使ってオブザーバーを削除し、notify メソッドを使ってすべてのサブスクライバーに通知できるようになりました。
このオブザーバブルを使って何かを作ってみましょう。Button と Switch という2つのコンポーネントだけで構成された、とてもシンプルなアプリがあります。
export default function App() {
return (
<div className="App">
<Button>Click me!</Button>
<FormControlLabel control={<Switch />} />
</div>
);
}
アプリケーションでのユーザー操作を追跡したいとします。ユーザーがボタンをクリックするか、スイッチを切り替えるたびに、そのイベントをタイムスタンプとともに記録します。記録するだけでなく、イベントが発生するたびに表示されるトースト通知も作成したいとします。
ユーザーが handleClick または handleToggle 関数を呼び出すたびに、これらの関数はオブザーバーの notify メソッドを呼び出します。notify メソッドは、handleClick または handleToggle 関数から渡されたデータをすべてのサブスクライバーに通知します。
まず、logger 関数と toastify 関数を作成しましょう。これらの関数は最終的に、notify メソッドから data を受け取ります。
import { ToastContainer, toast } from "react-toastify";
function logger(data) {
console.log(`${Date.now()} ${data}`);
}
function toastify(data) {
toast(data);
}
export default function App() {
return (
<div className="App">
<Button>Click me!</Button>
<FormControlLabel control={<Switch />} />
<ToastContainer />
</div>
);
}
現時点では、logger 関数と toastify 関数はオブザーバブルを認識していません。オブザーバブルはまだこれらの関数に通知できません。これらをオブザーバーにするには、オブザーバブルの subscribe メソッドを使って_登録_する必要があります。
import { ToastContainer, toast } from "react-toastify";
function logger(data) {
console.log(`${Date.now()} ${data}`);
}
function toastify(data) {
toast(data);
}
observable.subscribe(logger);
observable.subscribe(toastify);
export default function App() {
return (
<div className="App">
<Button>Click me!</Button>
<FormControlLabel control={<Switch />} />
<ToastContainer />
</div>
);
}
イベントが発生するたびに、logger 関数と toastify 関数に通知が届きます。あとは、実際にオブザーバブルに通知する関数である handleClick 関数と handleToggle 関数を実装するだけです。これらの関数は、オブザーバブルの notify メソッドを呼び出し、オブザーバーが受け取るデータを渡します。
import { ToastContainer, toast } from "react-toastify";
function logger(data) {
console.log(`${Date.now()} ${data}`);
}
function toastify(data) {
toast(data);
}
observable.subscribe(logger);
observable.subscribe(toastify);
export default function App() {
function handleClick() {
observable.notify("User clicked button!");
}
function handleToggle() {
observable.notify("User toggled switch!");
}
return (
<div className="App">
<Button>Click me!</Button>
<FormControlLabel control={<Switch />} />
<ToastContainer />
</div>
);
}
これで一連の処理が完成しました。handleClick 関数と handleToggle 関数がデータとともにオブザーバーの notify メソッドを呼び出し、その後、オブザーバーがサブスクライバー、この場合は logger 関数と toastify 関数に通知します。
ユーザーがどちらかのコンポーネントを操作するたびに、notify メソッドに渡したデータとともに、logger 関数と toastify 関数の両方に通知が届きます。
オブザーバーパターンはさまざまな方法で利用できますが、非同期のイベント駆動型データを扱う際に非常に便利です。特定のデータのダウンロードが完了したときや、ユーザーがメッセージボードに新しいメッセージを送信し、ほかのすべてのメンバーに通知するときに、特定のコンポーネントへ通知したい場合などに活用できます。
ケーススタディ
オブザーバブルパターンを使用する人気のライブラリに、RxJS があります。RxJS には、オブザーバブルパターンを使った豊富な組み込み機能とサンプルが用意されています。
メリット
オブザーバーパターンを使うと、関心の分離と単一責任の原則を徹底できます。オブザーバーオブジェクトはオブザーバブルオブジェクトと密結合しておらず、いつでも結合したり、結合を解除したりできます。オブザーバブルオブジェクトはイベントの監視を担当し、オブザーバーは受け取ったデータを処理します。
デメリット
オブザーバーが複雑になりすぎると、すべてのサブスクライバーに通知する際にパフォーマンスの問題が発生することがあります。