コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

Subscriptionパターン (プログラミング)

出典: フリー百科事典『ウィキペディア(Wikipedia)』

Subscriptionパターン(サブスクリプションパターン)とは、事前に設定されたサブスクライバー(購読者)が、特定のデータイベントの変化に基づいてサブジェクトから自動的に通知を受け取るデザインパターンである[1][2][3][4]。このパターンは、ソフトウェア開発において、特定のデータやイベントを効率的に管理するために利用されることが多い。オブザーバーパターンとは類似点があるが、Subscriptionパターンはサブスクライバーが自らに必要なデータやイベント通知を選択的に処理する点で異なっている。

Subscriptionパターンの起源は、オブジェクト指向プログラミングの進化に伴い、データの更新やイベントの変化に柔軟に対応する必要性が生じたところにある[5][6][7][8]。このパターンはリアクティブプログラミングとは異なり、データフローの双方向性や複雑なリアルタイム管理には重きを置いていないが、サブジェクトとサブスクライバー間のシンプルな通知メカニズムを提供する点で、特に静的なシステム設計に適している。

基本的な概念として、Subscriptionパターンではサブスクライバーが特定のサブジェクトに関連付けられ、システム設計時にサブスクライバーがどのデータやイベントの通知を受け取るかが決まる[6][7][8][9]。従来は、システムの稼働後にサブジェクトとサブスクライバーの動的な変更は行われないことが特徴であったが、現在では動的な購読管理も可能になり、システムの拡張性が向上している。

Subscriptionパターンの利用例としては、サーバーサイドクライアントサイドでの静的な通知システムが挙げられる[6][8][9][10]。このパターンは、リアルタイムストリーミングやプッシュ通知のような複雑なシステムとは異なり、事前に設定されたデータ処理を効率的に管理するための基盤として利用されている。

このページでは、Subscriptionパターンの概要、歴史と起源、基本概念、実装例、利点と欠点、関連するデザインパターンについて詳述する。

概要

[編集]

Subscriptionパターンとは、ソフトウェア設計において、あるデータやイベントの変化に対して、事前に登録されたサブスクライバーが自動的に通知を受けるメカニズムを提供するデザインパターンである[6][7][8][9]。このパターンは、システム内のコンポーネント間の依存関係を一定の範囲で低減し、効率的な情報配信を可能にする。Subscriptionパターンを採用することで、オブザーバーパターンと比較してシステムの拡張性や保守性が向上し、現在では動的なサブスクライバーの管理や通知処理が可能となっているが、依然として設計時にサブジェクトとサブスクライバーの関係性をあらかじめ構築することが一般的であり、これにより動作の予測可能性をもつシステム設計をサポートする。特にイベント駆動型のシステムで広く利用されている。

Subscriptionパターンとは

[編集]

Subscriptionパターンとは、プログラミングにおいて、あるオブジェクトサブジェクト)が特定の状態変化やイベントを発生させた際に、他のオブジェクト(サブスクライバー)に対して自動的に通知を行う仕組みを指す[1][2][11][12]。この通知は、サブジェクトとサブスクライバー間の依存関係を最小限に抑えることで、システムの柔軟性を高める役割を果たす。このパターンは、オブザーバーパターンと同様に、イベント駆動型のシステム設計で重要な位置を占める。

具体的に、このパターンでの依存関係の最小化は、サブスクライバーがサブジェクトの内部状態や具体的な実装にアクセスせず、あらかじめ定義されたインターフェースを通じて通知を受け取る仕組みによって実現される[6][7][8][9]。サブスクライバーは、サブジェクトが発生させる特定のデータやイベントのみを関心の対象とし、サブジェクトの具体的な情報の生成方法やその管理には依存しない。これにより、サブスクライバーは自らに必要なデータやイベントに応じた処理を選択的に行う一方で、サブジェクト側の変更や内部実装の詳細に左右されずに機能する。この設計によって、両者間の疎結合が保たれ、システムのモジュール性と柔軟性が向上する。

Subscriptionパターンの基本的な動作は、システムの設計時にサブジェクトとサブスクライバーの連携が事前に構築されており、サブジェクトがデータの変更や特定のイベントを検知した際に、登録されたサブスクライバーに対して一方向でデータ送信や通知を行うという流れである[1][2][11][12]。その後、各サブスクライバーは受け取ったデータや通知の中から、自らに必要なものを選択し、適切な処理を行う。これにより、サブスクライバーはデータの変更や通知内容に応じた個別のアクションを実行することができ、柔軟なデータ処理が可能となる。この仕組みはシステム全体の効率的な情報共有を支援する。また、システムの設計時にあらかじめサブジェクトとサブスクライバーの関係性が構築される特徴は、システム稼働時の動作に対する予測可能性を提供し、これにより安全なシステム開発が実現する。

初期のSubscriptionパターンには、システムの稼働後にはサブジェクトとサブスクライバー間の購読(サブスクライブ)に関する関係の変更はできない、または行われないという特徴が見られた[2][4][7][13]。これは購読の解除や再購読といった動的な運用に制約をかけるものであり、このような制約は、主に初期のコンピュータプログラミングの静的な設計思想に基づく[2][4][7][13]。しかし、現在ではこのような制約はなく、動的な運用が可能である[5][9]

歴史と起源

[編集]

Subscriptionパターンの起源は、オブジェクト指向プログラミングの初期に遡る。このパターンは、特に複雑なシステムにおいて、オブジェクト間の通信を効率的に管理するための手法として発展してきた。オブザーバーパターンの一部として始まったが、やがてその重要性が認識され、独立したデザインパターンとして扱われるようになった[2][3][4][7]

初期の導入と広がり

[編集]

Subscriptionパターンは、1990年代以降にオブジェクト指向プログラミングやデザインパターンの普及と共に、オブジェクト間でデータの変更やイベントの通知を効率的に管理する手法として発展した[2][3][4][13]。このパターンは、システム全体の依存関係を減らし、各サブスクライバーが自らに必要なデータのみを選択的に処理することで、柔軟なデータ処理を実現するために広く使用されるようになった。オブザーバーパターンと密接な関係にあり、イベント駆動型システムや通知機構を必要とする多くのソフトウェアで利用されていた。

システム稼働後の動的なサブスクリプション管理が行われないという特徴は、かつてのリソース制約やオブジェクト間の厳密な依存関係を軽減するために考えられたものである[2][3][4][8]。当時は、動的に依存関係を変更することが難しく、システムの安定性を確保するためにサブスクライバーの登録や削除をシステムの設計時に固定することが一般的だった。このためSubscriptionパターンでは、一度サブスクライバーが登録されると、システムの稼働中にその依存関係が変更されないという特徴が見られた。

Subscriptionパターンはその後も、より複雑なシステムにおけるデータ配信状態管理に活用されるようになったが、パブリッシュ/サブスクライブパターンやリアクティブプログラミングのような非同期通信や双方向のデータフローとは異なる特性を維持している[5][6][8][10]

Subscriptionパターンの普及と影響

[編集]

2000年代以降、Subscriptionパターンの基本的な構造には大きな変化は見られない[2][4][7][14]。しかし、技術の進化や新たなニーズに応じて、その利用シーンや応用範囲は拡大した。

まず、UI/UX(ユーザーインターフェース/ユーザーエクスペリエンス)の進化に伴い、Subscriptionパターンはユーザーインターフェースのリアクティブ(敏感)な更新において重要な役割を果たすようになった[15][16][17][18]。特に、JavaScriptや他のフロントエンドフレームワークにおいて、イベント駆動型のリアルタイムな更新が求められる場面で、Subscriptionパターンが活用されている。

また、非同期処理の増加に伴い、特に2010年代以降、サーバーサイドアプリケーションでもSubscriptionパターンは広く利用されている[8][9][19][20]。特に、通知システムやイベント処理の場面で、登録されたサブスクライバーに対する効率的な通知を実現するために、Subscriptionパターンが引き続き採用されている。

さらに、2010年代以降はリアクティブプログラミングとの連携が進む中で、Subscriptionパターンは非同期データストリームの管理や処理において重要な要素となった[5][6][8][19]。例えば、RxJavaやRxJSのようなリアクティブ拡張では、Subscriptionパターンがデータストリームの管理に利用され、サブスクライバーがオブザーバブルやパブリッシャーといったサブジェクトからの通知を受け取る仕組みが採用されている。この仕組みにより、データの流れをリアルタイムで制御し、イベント駆動型のシステムで効率的なデータ処理が実現されている。

リアクティブプログラミングでは、Subscriptionパターンに加え、バックプレッシャーや非同期制御といった高度な拡張機能も導入されている[5][6][8][19]。これにより、サブスクライバーのデータの処理能力に応じて供給量を調整することができ、システムのパフォーマンスが最適化される。このように、リアクティブプログラミングにおけるSubscriptionパターンは、単なる通知機構に留まらず、データフロー全体の制御を担う重要な基盤として機能している。

結果として、Subscriptionパターンの応用範囲は広がっているものの、その基本的な構造、すなわちサブジェクトとサブスクライバー間での通知機構の役割は変わらないままである[5][6][8][19]。リアクティブプログラミングにおける新たな拡張は、既存のパターンに機能を追加する形で、さらなる柔軟性とスケーラビリティをもたらしている。

このように、Subscriptionパターンは基本構造を維持しながらも、リアルタイム性や非同期処理が重要視されるシステムにおいて、その応用範囲は広がり続けている[5][6][8][19]

基本概念

[編集]

Subscriptionパターンは、オブジェクト指向プログラミングにおけるデザインパターンの一つであり、データやイベントの変化に応じて、サブスクライバーが通知を受け取る仕組みを提供する[1][2][3][13]。このパターンは、システムのコンポーネント間の疎結合を促進し、柔軟かつスケーラブルなアーキテクチャの構築を可能にする。サブスクライバーの登録はシステムの設計時に行われるが、現在ではシステムの実行中に変更を加えることも可能である。しかし、基本的にはサブジェクトとサブスクライバーはあらかじめ設定された関係に基づいて通知を処理する。Subscriptionパターンは、非同期処理の効率化や、特定のデータ変更、またはイベントに対して選択的に処理を行うシナリオで多く利用されている。

オブザーバーパターンとの関係

[編集]

Subscriptionパターンは、オブザーバーパターンと密接に関連している[1][2][4][8]。オブザーバーパターンもまた、特定のイベントや状態変化に応じて関連するオブジェクト(オブザーバー)に通知を送る仕組みであるが、Subscriptionパターンはこれと類似しつつ異なる特徴をもつ。両者の主要な違いは、オブザーバーパターンでは、オブザーバーはサブジェクトからの通知に対して全面的な処理を行うのに対し、Subscriptionパターンでは、サブスクライバーはサブジェクトから受信したデータやイベントの中から自らに必要なもののみを選択的に処理することを前提としており、そのための機能が備わっている点にある。この仕組みにより、サブジェクトとサブスクライバー間の依存関係を一定の範囲で削減し、システム全体の柔軟性を高めることが可能となる。

オブザーバーパターンが通知の仕組みに重点を置いているのに対し、Subscriptionパターンは、サブスクライバーがサブジェクトに関連付けられ、配信されるデータやイベントから必要なもののみを選択して処理するプロセスに重点を置いている[1][2][4][8]。サブジェクトとサブスクライバーの間には依存関係があるものの、サブスクライバーは受信したデータやイベントの中から自らが必要とするもののみを選択して処理するため、不要なデータやイベントを無視できる。このため、Subscriptionパターンは、全ての通知を受け取り処理するオブザーバーパターンと比較して、システムの柔軟性や効率性が向上し、システムへの負荷が軽減される。例えば、複数のサブジェクトからデータやイベントを購読(サブスクライブ)する場合でも、サブスクライバーは必要な情報のみを処理するため、システムのパフォーマンスに対する影響が少ない。

Subscriptionパターンの特徴

[編集]

Subscriptionパターンでは、サブジェクトがデータやイベントをサブスクライバーに配信し、サブスクライバーはその受信したデータの中から、自らに必要なものを選択的に処理する[2][4][8][9]。このパターンの初期の考え方において、購読の解除や再購読は、システムの設計時に固定的に設定されることが一般的であり、動的に行われるものではなかった。このため、サブスクライバーは動的に購読を解除する機能をもたなかったが、購読対象のデータやイベントをサブスクライバー自身が選別して処理することができる。現在のサブスクライバーは動的な購読の解除や再購読といった機能をもつが、サブジェクトがデータの変更やイベントの通知を一律に配信し、サブスクライバーがこれらの中から選択的に必要なもののみを処理するという仕組みは変化していない[6][8][9]

また初期の考え方においては、サブジェクトがサブスクライバーの管理を行うことも通常なく、サブスクライバーはシステム内での役割が事前に設定されていた[2][4][7][8]。この固定的な設計により、システムは予測可能な動作を維持し、モジュール性を保ちながら一定の拡張性を確保する。しかし、購読の管理がシステムの稼働後に柔軟に行われる前提がなかったため、動的なシステム変更には限界があった。現在のSubscriptionパターンはこの制約からも解放され、サブジェクトがサブスクライバーの購読の解除を動的に行うことや、サブスクライバーがデータの選別基準を動的に変更することなどが可能となっている[5][6][8][19]。現在のサブスクライバーはリアルタイムで自らの関心の対象を変更でき、必要に応じて選別基準を動的に更新することが可能である。これにより、システムはより柔軟でスケーラブルになり、サブスクライバーのニーズに応じたデータ配信が実現されている。

このように、Subscriptionパターンはパブリッシュ/サブスクライブパターンほど動的な購読管理機能はもたないが、サブスクライバーが特定のデータやイベントに対して選択的に処理を行うため、システム全体の柔軟性をある程度確保することが可能である[6][8][9]

主要なコンポーネント

[編集]

Subscriptionパターンを構成する主要なコンポーネントは、主に以下の三つである。

サブジェクト(Subject)

[編集]

サブジェクトは、データやイベントの変化を監視し、それらに変化が生じた際に全てのサブスクライバーに通知を行う役割を担う[5][6][8][19]。サブジェクトはサブスクライバーのリストを保持し、実装によっては新しいサブスクライバーの登録や、既存のサブスクライバーの解除も動的に管理することが可能である。過去には、このような管理機能は一般的ではなかったが、現在では動的な購読管理が広くサポートされているケースが多い。

サブスクライバー(Subscriber)

[編集]

サブスクライバーは、サブジェクトからのデータや通知を受け取るオブジェクトであり、受け取ったデータや通知に対して適切なアクションを実行する[5][6][8][19]。具体的には、サブスクライバーはサブジェクトが発行する全てのデータや通知の中から自らに必要なもののみを選択し、それに対してのみ処理を行う。過去には、サブスクライバーがどのサブジェクトに対して購読(サブスクライブ)を行うかはシステムの設計時にあらかじめ設定され、システム稼働後に動的な購読管理を行うことはできなかった。しかし、現在では多くの実装で動的な購読管理が可能となり、サブスクライバーがシステム稼働後に特定のデータやイベントに対して購読を行ったり、解除したりすることができるようになっている。これにより、無駄なデータ処理が省かれ、より効率的なデータ処理が実現される。

イベント(Event)

[編集]

イベントは、サブジェクトからサブスクライバーに通知される情報の単位である[1][2][11][12]。イベントは、システム内で発生した状態変化やアクションを表し、これがサブスクライバーに通知されることで、システム内の様々なプロセスが連動して動作する。

イベント駆動のメカニズム

[編集]

Subscriptionパターンの核心となるのが、イベント駆動のメカニズムである[1][2][6][19]。イベント駆動とは、システム内で発生するイベント(例:ユーザーアクション、データの更新、外部システムからの信号)に基づいて、処理を自動的に開始する仕組みを指す。このメカニズムにより、システムはリアクティブ(敏感)に動作し、特定の条件が満たされた場合にのみ処理を行うため、リソースの効率的な利用が可能となる。

Subscriptionパターンにおいては、サブジェクトがイベントを監視し、イベントが発生した際に、サブスクライバーに対して通知を行う[1][2][11][19]。サブスクライバーは、通知されたイベントに応じて、あらかじめ定義された処理を実行する。このプロセスは、システム全体が一連のイベントによって駆動する構造を作り出し、イベント駆動型アーキテクチャの基盤となっている。

イベント駆動のメカニズムを利用するSubscriptionパターンは、特定のデータフローやイベントの管理に適しており、システムの柔軟性や拡張性を高める一方で、リアルタイム性が求められるシステムにおいては、他のパターンと併用されることが多い[5][6][8][10]

実装例

[編集]

Subscriptionパターンは、様々な環境やプラットフォームで利用可能であり、特にサーバーサイドおよびクライアントサイドのシステムで幅広く採用されている[5][6][8][9]。このパターンを効果的に実装することで、システムのレスポンシブネス(応答性)を向上させ、柔軟な設計を実現することができる。

サーバーサイドの実装

[編集]

サーバーサイドでは、データダッシュボードのようなアプリケーションにおいてSubscriptionパターンが有効である[5][6][8][9]。この場合、サブスクライバーは自らが関心をもつ特定のデータ(例:重要業績評価指標センサーの情報)を選択的に受け取り、不要なデータは無視することができる。これにより、システムは必要な情報のみを効率的に処理することができ、システム全体の負荷を軽減しながら、重要なデータの提供が行われる。

Subscriptionパターンの初期の制約を再現した、例として1999年のPython 1.5.2では以下のようなコードになる。

class Subscriber:
    def __init__(self, name, interested_kpi):
        # サブスクライバーの名前と関心のあるKPIを初期化
        # 1999年当時、Pythonはオブジェクト指向プログラミングが主流になりつつあり、こうしたシンプルなクラス構造が採用されていた
        self.name = name
        self.interested_kpi = interested_kpi

    def notify(self, kpi_name, kpi_value):
        # 事前に指定されたKPIのみを処理する
        # 1999年ではf-stringが存在せず、文字列フォーマットには「%」演算子が主流であった
        if kpi_name == self.interested_kpi:
            print("%s が更新されたKPIを受け取りました: %s = %d" % (self.name, kpi_name, kpi_value))
        else:
            print("%s はKPIを無視しました: %s" % (self.name, kpi_name))

class KPIManager:
    def __init__(self):
        # サブスクライバーリストとKPI辞書の初期化
        # 1999年当時のPythonでは辞書やリストが既に存在し、シンプルなデータ管理が可能であった
        self.subscribers = []
        self.kpis = {}

    def register(self, subscriber):
        # システム稼働前にサブスクライバーを登録する
        # 当時はシステム稼働後のサブスクライバーの変更は想定されていなかったため、静的に登録を行う
        self.subscribers.append(subscriber)

    def update_kpi(self, kpi_name, kpi_value):
        # KPIを更新し、サブスクライバーに通知する
        # この仕組みは1999年当時も基本的に現在と同様で、KPIの更新に基づいて通知が行われる
        self.kpis[kpi_name] = kpi_value
        self.notify_subscribers(kpi_name, kpi_value)

    def notify_subscribers(self, kpi_name, kpi_value):
        # 全サブスクライバーに通知を送信する
        # 1999年当時から全サブスクライバーに一斉に通知を送る仕組みが一般的であり、選別のロジックはサブスクライバー側で行われる
        for subscriber in self.subscribers:
            subscriber.notify(kpi_name, kpi_value)

# システム稼働前にサブスクライバーを定義し、KPIを登録
# 1999年当時、動的なサブスクライバーの追加は一般的ではなく、事前に登録する形が主流
subscriber1 = Subscriber("Subscriber1", "KPI1")
subscriber2 = Subscriber("Subscriber2", "KPI2")

# KPIManagerを作成
manager = KPIManager()

# サブスクライバーを稼働前に登録
# サブスクライバーがシステム稼働後に変更されることはないため、ここで登録しておく
manager.register(subscriber1)
manager.register(subscriber2)

# システム稼働後、KPIが更新された際に通知
# 1999年では、システムが稼働後にKPIの更新が行われ、その結果に基づいて通知が行われる
manager.update_kpi("KPI1", 100)
manager.update_kpi("KPI2", 200)

# サブスクライバーの変更や追加は行われない
# 1999年当時、システム稼働後のサブスクライバーの変更は想定されておらず、追加や削除は行えない

このコードは、初期のSubscriptionパターンに基づいてサブジェクトとサブスクライバーの関係をモデル化している。サブスクライバーは、自らが関心をもつ特定のKPI(重要業績評価指標)を指定し、そのKPIが更新された際に通知を受ける。KPIManagerは複数のKPIを管理し、サブスクライバーに対して通知を行う役割を果たしている。

まず、Subscriberクラスはサブスクライバーの役割をもち、自らが関心をもつKPIを設定する。notifyメソッドでは、更新されたKPIが関心をもつものと一致する場合にのみ通知を処理し、それ以外のKPIは無視する。

次に、KPIManagerクラスは複数のKPIを管理し、サブスクライバーへの通知を行う。registerメソッドでサブスクライバーをシステム稼働前に登録し、update_kpiメソッドでKPIが更新された際にnotify_subscribersメソッドを呼び出し、全てのサブスクライバーに対して通知を行う。システム稼働後には、サブスクライバーの登録や削除は行われず、事前に設定されたサブスクライバーに対してのみ通知が行われる。

このように、サブジェクトとサブスクライバーの依存関係はシステムの設計時に決定され、システム稼働後には変更されないというSubscriptionパターンの初期実装を反映している。また、サブスクライバーはあらかじめ指定されたKPIに対してのみ反応するため、無関係なデータは処理されない。

一方、Subscriptionパターンの初期の制約から解放された、2023年のPython 3.12.3では以下のようなコードになる。

class Subscriber:
    def __init__(self, name, interested_kpi):
        self.name = name
        self.interested_kpi = interested_kpi

    def notify(self, kpi_name, kpi_value):
        # 1999年では%演算子を使って文字列のフォーマットを行っていた
        # 2023年では、より簡潔で効率的なf-stringsが使われている
        if kpi_name == self.interested_kpi:
            # f-stringsはPython 3.6で導入されたため、2023年の実装で利用可能
            print(f"{self.name} received updated KPI: {kpi_name} = {kpi_value}")  # f-stringsを使用
        else:
            print(f"{self.name} ignored KPI: {kpi_name}")  # f-stringsを使用

class KPIManager:
    def __init__(self):
        self.subscribers = []
        self.kpis = {}

    # 1999年の実装では、サブスクライバーの追加・削除は稼働前のみ可能であったが、
    # 2023年の実装では、システム稼働後にも動的に追加・削除が可能
    def register(self, subscriber):
        self.subscribers.append(subscriber)

    def unregister(self, subscriber):
        self.subscribers.remove(subscriber)

    def update_kpi(self, kpi_name, kpi_value):
        self.kpis[kpi_name] = kpi_value
        self.notify_subscribers(kpi_name, kpi_value)

    # 1999年ではforループが基本であり、内包表記やジェネレータは使用できなかったが、
    # 2023年の実装では、内包表記やジェネレータが利用可能
    def notify_subscribers(self, kpi_name, kpi_value):
        # 内包表記を使用して、2023年の効率的な実装例を示す
        [
            subscriber.notify(kpi_name, kpi_value)
            for subscriber in self.subscribers
        ]

# サブスクライバーを定義し、動的にKPIを登録
# 1999年の実装ではサブスクライバーの追加はシステム稼働前にのみ行えたが、
# 2023年のこの実装では、サブスクライバーをシステム稼働中にも追加できる
subscriber1 = Subscriber("Subscriber1", "KPI1")
subscriber2 = Subscriber("Subscriber2", "KPI2")

# KPIManagerを作成
manager = KPIManager()

# サブスクライバーを動的に登録
# 2023年の実装ではシステム稼働中にもサブスクライバーの登録・解除が可能
manager.register(subscriber1)
manager.register(subscriber2)

# KPIが更新された際に通知
# 1999年と同様に、KPIが更新されると通知が行われる
manager.update_kpi("KPI1", 100)
manager.update_kpi("KPI2", 200)

# システム稼働後にサブスクライバーを動的に解除
# 1999年の実装ではサブスクライバーの解除は行えなかったが、
# 2023年ではシステム稼働後にサブスクライバーの解除が可能
manager.unregister(subscriber1)

# KPIが再度更新された際に通知
# サブスクライバーが削除されているため、削除されたサブスクライバーには通知されない
manager.update_kpi("KPI1", 300)
manager.update_kpi("KPI2", 400)

このコードでは、1999年の実装と異なり、サブスクライバーの登録がシステムの稼働後にも動的に行えるようになっている。ただし、Subscriptionパターンの基本部分として、サブジェクトはサブスクライバーに対して一律にデータやイベントを送信する点は変わっておらず、サブスクライバー側で必要なデータを選別して処理する仕組みは維持されている。これにより、サブスクライバーは自らの関心に基づいたデータのみを処理し、無駄な処理を避けることが可能になっている。

クライアントサイドの実装

[編集]

クライアントサイドでは、Subscriptionパターンが有効な例として状態監視システムなどが挙げられる[6][8][9]工場機械サーバーの状態を監視し、特定の機器に異常が発生した場合、サブスクライバーが通知を受け取って対応する。これにより、複数のサブスクライバーがそれぞれ必要な情報に応じて柔軟に対応することができ、リアルタイムでの監視が可能になる。Subscriptionパターンは、これらのシステムにおいて、効率的なデータ処理と柔軟性を提供する設計として特に効果的である。

Subscriptionパターンの初期の制約を再現した、例として1999年のJava 1.2では以下のようなコードになる。

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

// サブジェクトクラス(監視対象のサーバー)
class Server {
    private String name;
    private boolean status; // サーバーの状態(正常: true, 異常: false)
    private List<Subscriber> subscribers; // サブスクライバーリスト

    public Server(String name) {
        this.name = name;
        this.status = true;
        this.subscribers = new ArrayList<>();
    }

    // サブスクライバーの登録
    public void subscribe(Subscriber subscriber) {
        subscribers.add(subscriber);
    }

    // サーバーの状態を変更し、異常発生時に通知
    public void setStatus(boolean status) {
        this.status = status;
        if (!status) { // 異常が発生した場合のみ通知
            notifySubscribers();
        }
    }

    // サブスクライバーに通知を送信
    private void notifySubscribers() {
        // 1999年ではストリームAPIやラムダ式が存在しないため、ループで通知する
        for (Subscriber subscriber : subscribers) {
            subscriber.update(name + " サーバーに異常が発生しました。");
        }
    }
}

// サブスクライバークラス(監視担当者)
class Subscriber {
    private String name;

    public Subscriber(String name) {
        this.name = name;
    }

    // サーバーからの通知を受け取る
    public void update(String message) {
        // 1999年ではString.formatもf-stringsも存在しないため、+演算子を用いた単純な文字列連結で処理する
        System.out.println(name + "が通知を受信しました: " + message);
    }
}

// メインクラス
public class MonitoringSystem {
    public static void main(String[] args) {
        // サーバーとサブスクライバーを作成
        Server server1 = new Server("Server1");
        Server server2 = new Server("Server2");

        Subscriber subscriber1 = new Subscriber("監視担当者1");
        Subscriber subscriber2 = new Subscriber("監視担当者2");

        // サーバーにサブスクライバーを登録
        server1.subscribe(subscriber1);
        server2.subscribe(subscriber2);

        // 1999年では、サブスクライバーの登録や削除が動的には行われない
        // 事前に登録したサブスクライバーのみが通知を受け取る

        // サーバーの状態をシミュレート
        server1.setStatus(false); // Server1に異常が発生
        server2.setStatus(false); // Server2に異常が発生
    }
}

このコードは、初期のSubscriptionパターンに基づいたサーバー監視システムの実装例である。サーバーが異常状態に陥った場合、事前に登録されたサブスクライバーに対して通知を送信する仕組みを構築している。サブジェクトはサーバーであり、サブスクライバーは監視担当者として設定されている。

サブジェクトであるServerクラスは、サーバーの名前と状態をもち、サーバーの異常を検知した際にサブスクライバーに通知を送信する。サブスクライバーはSubscriberクラスで定義され、サーバーからの通知を受信して対応する。サーバーの状態が異常になった場合、notifySubscribersメソッドを通じて登録された全てのサブスクライバーに通知を送信する。サブスクライバーは受信した通知に基づいて必要な処理を行う。

このコードは、サブスクライバーの登録や通知の送信がシステム稼働前に設定され、動的なサブスクライバー管理が行われないという点で、当時の制約を反映している。

一方、Subscriptionパターンの初期の制約から解放された、2023年のJava 21では以下のようなコードになる。

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

// サブスクライバークラスはサーバーの状態を監視する役割を担う
class Subscriber {
    private String name;
    private String interestedServer;

    public Subscriber(String name, String interestedServer) {
        this.name = name;
        this.interestedServer = interestedServer;
    }

    // 通知を受け取る際に、関心のあるサーバーの異常のみを処理する
    public void notify(String serverName, String serverStatus) {
        // 1999年では%演算子を使用して文字列フォーマットを行っていた
        // 2023年ではf-stringsに相当するフォーマットが利用可能となっている
        if (serverName.equals(interestedServer)) {
            System.out.println(name + " received alert: " + serverName + " status = " + serverStatus);
        } else {
            System.out.println(name + " ignored alert: " + serverName);
        }
    }

    public String getInterestedServer() {
        return interestedServer;
    }
}

// ServerMonitorクラスはサーバーの状態を監視し、サブスクライバーに通知を送る
class ServerMonitor {
    private List<Subscriber> subscribers = new ArrayList<>();
    private List<String> servers = new ArrayList<>();

    // 1999年では、サブスクライバーの追加はシステム稼働前にのみ行えた
    // 2023年の実装では、動的にサブスクライバーを追加・削除できる
    public void register(Subscriber subscriber) {
        subscribers.add(subscriber);
    }

    // サブスクライバーの動的な削除も可能
    public void unregister(Subscriber subscriber) {
        subscribers.remove(subscriber);
    }

    // サーバーの状態を更新し、全サブスクライバーに一律で通知を送る
    public void updateServerStatus(String serverName, String serverStatus) {
        servers.add(serverName);  // サーバーの状態を更新
        notifySubscribers(serverName, serverStatus);  // 全てのサブスクライバーに通知
    }

    // 事前に登録された全てのサブスクライバーに一律に通知を送信
    private void notifySubscribers(String serverName, String serverStatus) {
        // 1999年ではforループを使用していたが、2023年では内包表記を使用できる
        // 内包表記で一律通知を効率的に行うが、基本的な一律通知の動作は変わらない
        for (Subscriber subscriber : subscribers) {
            subscriber.notify(serverName, serverStatus);
        }
    }
}

// メインの実行部分
public class ServerMonitoringSystem2023 {
    public static void main(String[] args) {
        // サブスクライバーを定義し、関心のあるサーバーを設定する
        Subscriber subscriber1 = new Subscriber("Subscriber1", "Server1");
        Subscriber subscriber2 = new Subscriber("Subscriber2", "Server2");

        // ServerMonitorを作成し、サブスクライバーを登録
        ServerMonitor monitor = new ServerMonitor();

        // 2023年の実装では動的なサブスクライバーの登録・削除が可能
        monitor.register(subscriber1);
        monitor.register(subscriber2);

        // サーバーの状態が更新された際に一律で通知を送る
        monitor.updateServerStatus("Server1", "Critical");
        monitor.updateServerStatus("Server2", "Operational");

        // サブスクライバーを動的に削除することができる
        monitor.unregister(subscriber1);

        // 削除後も残ったサブスクライバーには通知が行われる
        monitor.updateServerStatus("Server1", "Operational");
        monitor.updateServerStatus("Server2", "Critical");
    }
}

このコードは、現在のSubscriptionパターンに基づいたサーバー監視システムの例である。1999年のSubscriptionパターンでは、サブスクライバーの追加や削除はシステムの稼働前にのみ行えたが、現在のSubscriptionパターンでは、システムの稼働中にもサブスクライバーを動的に追加・削除することが可能である。これにより、サブスクライバーのニーズに応じたリアルタイムでの変更や調整が行え、購読の必要がなくなったサブスクライバーはシステムの稼働中に購読を解除することができ、システムの柔軟性向上に寄与している。

一方で、1999年でも現在でも、サブジェクトが全てのサブスクライバーに一律に通知を送り、サブスクライバーは受け取った通知の中から自らに必要な情報のみを選別して処理するという点は変わっていない。

プラットフォーム別の実装例

[編集]

Subscriptionパターンの実装は、想定するプラットフォームに応じて様々な形態を取る。以下に、いくつかの主要なプラットフォームでの実装例を示す。

ローカル通知システム

[編集]

ローカル通知システムは、Subscriptionパターンの典型的なユースケースであり、特にアプリケーション内部での状態変化やイベント発生時に、その変化を特定のサブスクライバーが処理する仕組みとして機能する[6][8][9][19]。このシステムは、主にデスクトップアプリケーションスタンドアロンのソフトウェアで活用されている。システム内での通知の範囲がローカルに限定されているため、サブジェクトからサブスクライバーへの一方向の関係が効果的に利用されている。

ローカル通知システムにおいて、サブジェクトが全てのサブスクライバーに一律に通知を送信し、各サブスクライバーはその中から担当する情報を選別して処理する[6][8][9][19]。これにより、個々のサブスクライバーが自らの役割に応じて必要な処理のみを行うことができる。また、ローカル環境でのパフォーマンス向上が期待でき、ネットワークの遅延や他の外部要因に左右されない利点がある。他のパターン、特にパブリッシュ/サブスクライブパターンが必要とされるような大規模な分散システムでは、異なるパターンが推奨されるが、ローカル通知システムではSubscriptionパターンが最適といえる。

UI更新のリアクティブ管理

[編集]

UI(ユーザーインターフェース)の更新におけるリアクティブ(敏感)な管理では、ユーザーインターフェースの状態が変化した際に、その変化を即座に反映させるためにSubscriptionパターンを使用する[6][8][9][19]。特にフロントエンドWebアプリケーションやデスクトップアプリケーションでは、ユーザーの操作やデータの変更に応じて動的にUIを更新することが求められる。このシナリオで、サブジェクトがUIの状態を監視し、その変更をサブスクライバーである各UIコンポーネントに通知することで、リアクティブ(敏感)な更新が実現される。

このシナリオでは、UIコンポーネントがアプリケーション全体の更新通知を受け取る度に、自らが担当する部分のみを選別して更新処理を行う[6][8][9][19]。リアクティブプログラミングの原則に基づき、UIのレスポンシブネス(応答性)を向上させ、ユーザーエクスペリエンスを向上させる。しかし、大規模な分散型システムや複数のマイクロサービス間でのデータ通信が求められる場合には、パブリッシュ/サブスクライブパターンがより適している。

データフィードの選別配信システム

[編集]

データフィードの選別配信システムでは、複数のサブスクライバーが興味をもつ異なるデータセットを受け取る(処理する)場面でSubscriptionパターンが活用される[6][8][9][19]。このシステムでは、サブジェクトが多様なデータを一律に配信し、個々のサブスクライバーはその多様なデータの中から自らに必要なデータセットを選別する。この仕組みは、財務データやリアルタイムのログ情報監視システムなど、大量のデータが絶え間なく更新される場面で効果を発揮する。

データフィードシステムでは、サブスクライバーが特定のフィルタリング条件に基づいてデータを選別できるため、効率的なデータ処理が可能となる[6][8][9][19]。例えば、各サブスクライバーが財務データの中から特定の銘柄に関する情報のみを選別するシステムなどが挙げられる。他の分散型システムでは、パブリッシュ/サブスクライブパターンが推奨されることもあるが、このような選別配信システムではSubscriptionパターンが適している。

これらの実装例を通じて、Subscriptionパターンが様々な環境でどのように応用されているのかを理解できる。各プラットフォームにおける実装は、そのプラットフォーム特有の機能や制約を考慮しつつ、パターンの基本的な原則を適用することで、最適なシステムを構築することが可能である。

利点と欠点

[編集]

Subscriptionパターンは、様々なシステムで広く利用されているが、その実装には利点と欠点の両方が存在する。以下では、Subscriptionパターンの利点と欠点、およびパフォーマンスに関する考慮点について詳述する。

利点

[編集]

疎結合の促進

[編集]

Subscriptionパターンでは、サブジェクトとサブスクライバー間の依存関係は、システムの設計時に決定されることが基本であり、これにより依存関係が一定の範囲で抑えられる[6][8][9][19]。この疎結合により、各コンポーネントを独立して開発・保守することが可能となり、システム全体の柔軟性や拡張性が向上する。さらに、現在のSubscriptionパターンでは、システム稼働後に動的なサブスクライバーの管理を行うことが可能となっており、リアルタイムでの変更や調整がスムーズに行える。これにより、新しい機能やコンポーネントを追加する際にも、既存のシステムに大きな影響を与えることなく拡張することができる。

スケーラビリティの向上

[編集]

Subscriptionパターンは、大規模なシステムにおいても効果的に機能する[6][8][9][19]。サブスクライバーの数が増加しても、サブジェクトは全てのサブスクライバーに一律で通知を送信し、各サブスクライバー側で選択的にデータを処理するため、システムの拡張性が保たれる。また、現在のSubscriptionパターンでは、システム稼働後に動的なサブスクライバーの追加・削除を行えるため、リソースの効率的な管理や負荷の分散が容易になっている。これにより、システム全体のスケーラビリティが向上する。

リアルタイム性の実現

[編集]

Subscriptionパターンは、リアルタイムでのデータ更新やイベント通知を必要とするシステムにおいて有効である[6][8][9][19]。サブジェクトは、データやイベントに変化が生じた際、即座にサブスクライバーへ通知を送信する。このリアルタイム性は、リアクティブプログラミングのサブジェクトのような双方向通信を必要とせず、一方向の通知でシステムのレスポンシブネス(応答性)を確保できるような、シンプルな設計が可能なシステムにおいて有用である。これにより、ユーザーは常に最新の情報を遅延なく受け取ることができ、リアルタイム性が求められるシステムにおいて高い効果を発揮する。

コードの簡素化

[編集]

Subscriptionパターンでは、同一のサブジェクトに対して複数のサブスクライバーを登録し、個々のサブスクライバーのニーズに応じて異なる処理を行うことが可能である[6][8][9][19]。これにより、同じサブジェクトからの通知を受け取りながらも、サブスクライバー毎に異なるアクションを取ることができるため、重複するコードを書く必要がなくなり、開発効率が向上する。

保守性とテスト容易性の向上

[編集]

Subscriptionパターンでは、サブジェクトとサブスクライバーが緩やかに結合されているため、個別のコンポーネントを独立してテストや保守することが容易になる[6][8][9][19]。この特性により、エラーが発生した際には、影響範囲を局所化しやすく、迅速に問題解決を行える。また、システムの一部を変更・更新する際にも、他の部分に影響を与えるリスクが低いため、メンテナンスコストを低減できる。

欠点

[編集]

デバッグの困難さ

[編集]

Subscriptionパターンでは、サブジェクトからの通知が全てのサブスクライバーに一律に送信されるため、各サブスクライバーがどのような処理を行っているかを把握しにくい場合がある[6][8][9][19]。特に、大規模なシステムや複雑な通知処理を行うシステムでは、エラーやバグの発生原因を特定するのに時間がかかることがある。サブスクライバーの数が増えるほど、どのサブスクライバーがどのように動作しているのかを追跡することが難しくなり、デバッグが困難になる可能性がある。

予期せぬ副作用

[編集]

Subscriptionパターンでは、サブジェクトが通知を発行した際に、全てのサブスクライバーに一律で通知が送信される[6][8][9][19]。このため、全てのサブスクライバーが同じ通知を受け取るが、各サブスクライバーがそれぞれ異なるタイミングや条件で通知を処理する可能性がある。このような非同期的な処理は、競合状態やデータの不整合を引き起こすことがあるため、サブスクライバーの処理フローを設計時に十分に管理することが重要となる。

リソース消費の増加

[編集]

Subscriptionパターンでは、通知が全てのサブスクライバーに一律で送信されるため、特にローカル通知システムのように依存関係が一つの機器で完結している場合、かつサブスクライバーの数が多い場合に計算資源をより多く必要とすることになる[6][8][9][19]。この結果、システム全体のリソース消費が増加し、適切な制御やフィルタリングが行われていない場合、不要なデータ処理がシステムのパフォーマンスを低下させることがある。これを防ぐためには、通知内容の最適化や効率的なフィルタリングが必要となる。または、このような場合はより効率的な他のデザインパターンを導入することも検討するべきである。

複雑性の増大

[編集]

Subscriptionパターンは比較的シンプルな設計をもつため、小規模なシステムや単純な要件においては、過剰な設計とはならず、むしろ効率的な選択肢となる[6][8][9][19]。しかし、システムが大規模化し、サブスクライバーが増加する場合には、依存関係の管理や通知の追跡が煩雑になることがあるため、そのような場合には適切な設計のし直しと管理が必要となり、追加のコストがかかる場合がある。ただし、初めから大規模化を見越して適切に設計されたSubscriptionパターンは、システムの柔軟性と保守性を引き続き維持することができる。

依存関係の追跡の難しさ

[編集]

Subscriptionパターンでは、サブスクライバーがサブジェクトから一律に通知を受け取るため、どのサブスクライバーがどの通知に反応しているのかを追跡することが難しくなることがある[6][8][9][19]。特に、ローカル環境で複数のサブスクライバーが同時に異なるデータやイベントを処理している場合、依存関係の管理が複雑化する。このような複雑性を防ぐためには、依存関係を明確にドキュメント化し、管理する仕組みが重要である。

パフォーマンス上の考慮点

[編集]

イベントの最適化

[編集]

Subscriptionパターンでは、サブジェクトが一律に全てのサブスクライバーに通知を送信するため、発行されるイベントの頻度や量がシステム全体の負荷に大きく影響する[6][8][9][19]。特に、大規模なシステムでは必要以上のイベント通知がリソースを消費し、パフォーマンスに悪影響を及ぼすことがある。このため、サブジェクト側でイベントの発行頻度や内容を適切に管理し、無駄な通知を減らす設計が求められる。必要に応じてイベントの優先度を設定し、重要な通知を優先的に配信することで、システム全体のパフォーマンスを最適化することができる。

非同期処理とスレッド管理

[編集]

Subscriptionパターンにおいても、非同期処理を利用することで、通知処理が行われる間に他のタスクの処理が遅延することを防ぐ[6][8][9][19]。しかし、スレッド管理が適切に行われない場合、パフォーマンスの低下やシステム不安定化のリスクがある。スレッドプールの設定や非同期タスクの管理は、特に通知が頻繁に発生する大規模なシステムでは重要となる。

メモリ使用量の監視

[編集]

システムが大規模化し、サブスクライバーの数が増えるとメモリ使用量が増大する傾向にある[6][8][9][19]。特に、ローカル通知システムのようにサブジェクトとサブスクライバーの依存関係が特定の機器上で完結している場合、メモリリークのリスクが高まる可能性がある。サブスクライバーが不要になった際には、メモリを解放するための仕組みを適切に実装することが求められる。このようなリソース管理を行うことで、システムの健全性とパフォーマンスを維持できる。

通知頻度の管理

[編集]

Subscriptionパターンでは、サブジェクトからの通知が全てのサブスクライバーに一律で送信されるため、頻繁な通知がシステムの負荷を増加させる可能性がある[6][8][9][19]。サブスクライバーは通知されたデータの中から必要な情報のみを処理するが、通知が過剰になると、システムのパフォーマンスが低下する可能性がある。システムの規模や用途に応じて、イベントの発生頻度や通知タイミングを適切に調整する設計が求められる。

エラーハンドリングと再試行機構

[編集]

サブジェクトからの通知がエラーによって処理されなかった場合、適切なエラーハンドリングを行わないと、サブスクライバーが通知を処理できず、システムの整合性が損なわれる可能性がある[6][8][9][19]。サブスクライバーがエラーに応じて再試行を行い、確実に通知が処理される仕組みを設計に組み込むことで、信頼性の高いシステムが実現される。

モニタリングとロギング

[編集]

サブジェクトとサブスクライバーの間の通知処理を正確に追跡し、システム全体の状態を把握するためには、適切なモニタリングとロギングを導入する必要がある[6][8][9][19]。特に、大規模なシステムでは、サブスクライバーの挙動やイベント通知の流れを監視することで、問題の早期発見と迅速な対処が可能となる。

これらのパフォーマンス上の考慮点を適切に管理・最適化することで、Subscriptionパターンを用いたシステムが高い効率性と信頼性をもって運用されることが可能となる。

関連するデザインパターン

[編集]

Subscriptionパターンは、ソフトウェア設計における他のデザインパターンとも深い関連がある。これらの関連パターンは、それぞれ異なるコンテキスト(文脈)で使用されるが、イベント駆動型のシステム設計やリアルタイム性の向上を目的としている点で共通している。以下では、Subscriptionパターンと関連性の高いデザインパターンとして、オブザーバーパターン、パブリッシュ/サブスクライブパターン、リアクティブプログラミングを紹介する。

オブザーバーパターン

[編集]

オブザーバーパターンは、Subscriptionパターンの基礎となるデザインパターンである[1][2][21][22]。このパターンでは、あるオブジェクト(サブジェクト)が状態を変更した際に、依存するオブジェクト(オブザーバー)に対して自動的に通知を行う仕組みを提供する。オブザーバーパターンでは、サブジェクトとオブザーバーの間で一対多の関係が構築され、サブジェクトの状態が変化すると、全てのオブザーバーがその変化を反映するように設計されている。

Subscriptionパターンは、オブザーバーパターンをより柔軟にしたものである[1][2][21][22]。オブザーバーパターンが主にオブジェクト間の直接的な通知を扱うのに対し、Subscriptionパターンの特徴は、サブスクライバーが通知されたイベントの中から自ら選択したものにのみ反応できる点にある。これにより、システム全体の柔軟性が大きく向上する。また、Subscriptionパターンでは発行者と受信者の関係が緩やかな疎結合に保たれるため、システムの拡張や保守が容易になる。

オブザーバーパターンは、GUIアプリケーションのイベント処理や、MVC(Model-View-Controller)アーキテクチャにおけるViewとModelの連携において、広く使用されている[1][2][21][22]。このパターンを理解することで、Subscriptionパターンの応用や利点をより深く理解することができる。

パブリッシュ/サブスクライブパターン

[編集]

パブリッシュ/サブスクライブパターン(Publish/Subscribe Pattern、通称Pub/Sub)は、Subscriptionパターンと非常に類似したデザインパターンであり、イベント駆動型のアーキテクチャで広く採用されている[8][12][23][24]。このパターンでは、イベントの発行者(パブリッシャー)と受信者(サブスクライバー)間にメッセージブローカーが存在し、パブリッシャーはメッセージブローカーにイベントを送信し、サブスクライバーはメッセージブローカーを通じてそのイベントを受信する。

Pub/Subパターンの最大の特徴は、発行者と受信者が直接的な依存関係をもたず、完全に疎結合である点である[8][12][23][24]。発行者は、どのサブスクライバーがイベントを受信するかを知らず、同様にサブスクライバーも、どのパブリッシャーがイベントを発行しているかを知らない。この非同期かつ分散型のアプローチにより、システムは大規模で複雑な環境においても、柔軟かつスケーラブルに動作することができる。

パブリッシュ/サブスクライブパターンは、クラウドベースのシステムやマイクロサービスアーキテクチャにおいて、イベント駆動型通信を実現するために広く利用されている[8][6][24][25]。例えば、Google Cloud Pub/SubAmazon SNSなどのクラウドサービスは、このパターンに基づいて構築されており、分散システム間での信頼性の高いメッセージングを提供している。

リアクティブプログラミング

[編集]

リアクティブプログラミングは、Subscriptionパターンの概念をさらに発展させたプログラミングパラダイムであり、データの流れやイベントの伝播を中心にシステムを構築する手法である[5][8][19][23]。リアクティブプログラミングでは、データが流れる度にそれに応じた処理が自動的に実行されるため、システムは非同期でリアルタイムに反応することができる。

リアクティブプログラミングの中核となるのは「リアクティブストリーム」であり、これはデータの流れをストリームとして捉え、サブスクライバーがストリーム内のデータやイベントをリアルタイムで受信・処理する仕組みである[5][8][19][23]。この手法は、特に大規模なデータ処理や複雑なユーザーインターフェースをもつアプリケーションで効果を発揮し、システム全体が動的かつ効率的に動作するようになる。

リアクティブプログラミングは、RxJavaやReactorといったリアクティブ拡張を通じて実装されることが多く、これにより複雑な非同期処理やイベント駆動型のデータフローを簡潔に表現することが可能となる[5][8][19][23]。また、リアクティブシステムは、非同期処理やリアルタイム性が求められる環境で非常に効果的であり、モダンなWebアプリケーションや分散システムで広く採用されている。

これらの関連するデザインパターンを理解することで、Subscriptionパターンの適用範囲や応用方法をより深く学び、設計するシステムに最適なパターンを選択するための知識が得られる。オブザーバーパターン、パブリッシュ/サブスクライブパターン、リアクティブプログラミングはいずれも、Subscriptionパターンの考え方を補完し、さらなる可能性を提供する重要なコンセプトである。

出典

[編集]
  1. ^ a b c d e f g h i j k l Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994-10-31) (英語). Design Patterns: Elements of Reusable Object-Oriented Software. Pearson Education. ISBN 978-0-321-70069-8. https://www.google.co.jp/books/edition/_/6oHuKQe3TjQC?hl=ja&sa=X&ved=2ahUKEwj0lLv6g7OIAxWYafUHHWwAJYUQ7_IDegQIAxBn 
  2. ^ a b c d e f g h i j k l m n o p q r s t Freeman, Eric; Robson, Elisabeth; Freeman, Elisabeth; Sierra, Kathy; Bates, Bert (2004-10-25) (英語). Head First Design Patterns. "O'Reilly Media, Inc.". ISBN 978-0-596-00712-6. https://www.google.co.jp/books/edition/Head_First_Design_Patterns/GGpXN9SMELMC?hl=ja&gbpv=1&dq=Head+First+Design+Patterns&printsec=frontcover 
  3. ^ a b c d e Fowler, Martin; Beck, Kent (1999) (英語). Refactoring: Improving the Design of Existing Code. Addison-Wesley Professional. ISBN 978-0-201-48567-7. https://www.google.co.jp/books/edition/Refactoring/UTgFCAAAQBAJ?hl=ja&gbpv=1&dq=Refactoring:+Improving+the+Design+of+Existing+Code&printsec=frontcover 
  4. ^ a b c d e f g h i j k Fowler, Martin (2003) (英語). Patterns of Enterprise Application Architecture. Addison-Wesley. https://www.google.co.jp/books/edition/_/JCvpzgEACAAJ?hl=ja&sa=X&ved=2ahUKEwi1idSyq7OIAxWyUPUHHQwuCZ8Qre8FegQIAxAr 
  5. ^ a b c d e f g h i j k l m n o p Tomasz, Nurkiewicz; Christensen, Ben (2016) (英語). Reactive Programming with RxJava. O'Reilly Media, Incorporated. ISBN 978-1-4919-3164-6. https://www.google.co.jp/books/edition/Reactive_Programming_with_RxJava/0QLIswEACAAJ?hl=ja 
  6. ^ a b c d e f g h i j k l m n o p q r s t u v w x y z aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq Kleppmann, Martin (2017-03-16) (英語). Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems. "O'Reilly Media, Inc.". ISBN 978-1-4919-0311-7. https://www.google.co.jp/books/edition/Designing_Data_Intensive_Applications/zFheDgAAQBAJ?hl=ja&gbpv=1&dq=Designing+Data-Intensive+Applications:+The+Big+Ideas+Behind+Reliable,+Scalable,+and+Maintainable+Systems&printsec=frontcover 
  7. ^ a b c d e f g h i Evans, Eric (2004) (英語). Domain-driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley Professional. ISBN 978-0-321-12521-7. https://www.google.co.jp/books/edition/Domain_driven_Design/xColAAPGubgC?hl=ja&gbpv=0 
  8. ^ a b c d e f g h i j k l m n o p q r s t u v w x y z aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq ar as at au av aw ax ay az ba Newman, Sam (2015-02-02) (英語). Building Microservices: Designing Fine-Grained Systems. "O'Reilly Media, Inc.". ISBN 978-1-4919-5033-3. https://www.google.co.jp/books/edition/Building_Microservices/jjl4BgAAQBAJ?hl=ja&gbpv=1&dq=Building+Microservices:+Designing+Fine-Grained+Systems&printsec=frontcover 
  9. ^ a b c d e f g h i j k l m n o p q r s t u v w x y z aa ab ac ad ae af ag ah Richardson, Chris (2018-10-27) (英語). Microservices Patterns: With examples in Java. Simon and Schuster. ISBN 978-1-63835-632-5. https://www.google.co.jp/books/edition/Microservices_Patterns/QTgzEAAAQBAJ?hl=ja&gbpv=0 
  10. ^ a b c Taylor, Hugh; Yochem, Angela; Phillips, Les; Martinez, Frank (2009-02-17) (英語). Event-Driven Architecture: How SOA Enables the Real-Time Enterprise. Pearson Education. ISBN 978-0-321-63515-0. https://www.google.co.jp/books/edition/Event_Driven_Architecture/g1318W0CIm4C?hl=ja&gbpv=0 
  11. ^ a b c d Fowler, Martin (2017-07-20) (英語). Patterns of Enterprise Application Architecture. CreateSpace Independent Publishing Platform. ISBN 978-1-5489-1919-1. https://www.google.co.jp/books/edition/_/ehplswEACAAJ?hl=ja&sa=X&ved=2ahUKEwja5ZXdqpeIAxUlk68BHXmICDIQre8FegQIAhAz 
  12. ^ a b c d e Hohpe, Gregor; Woolf, Bobby (2012-03-09) (英語). Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Addison-Wesley. ISBN 978-0-13-306510-7. https://www.google.co.jp/books/edition/Enterprise_Integration_Patterns/qqB7nrrna_sC?hl=ja&gbpv=0 
  13. ^ a b c d Meyer, Bertrand (1997) (英語). Object-oriented Software Construction. Prentice Hall PTR. ISBN 978-0-13-629155-8. https://www.google.co.jp/books/edition/Object_oriented_Software_Construction/xls_AQAAIAAJ?hl=ja&gbpv=1&bsq=Object-Oriented+Software+Construction&dq=Object-Oriented+Software+Construction&printsec=frontcover 
  14. ^ Russ Olsen (2009) (英語). Design patterns in Ruby. Addison-Wesley. https://search.worldcat.org/ja/title/837998928 2024年9月8日閲覧。 
  15. ^ Alex Banks, Eve Porcello (2017) (英語). Learning React: functional web development with React and Redux. O'Reilly. https://search.worldcat.org/ja/title/992518111 2024年9月8日閲覧。 
  16. ^ Flanagan, David (2020-05-14) (英語). JavaScript: The Definitive Guide: Master the World's Most-Used Programming Language. "O'Reilly Media, Inc.". ISBN 978-1-4919-5198-9. https://www.google.co.jp/books/edition/JavaScript_The_Definitive_Guide/NPbkDwAAQBAJ?hl=ja&gbpv=1&printsec=frontcover 
  17. ^ Macrae, Callum (2018-02-23) (英語). Vue.js Up & Running: Building Accessible and Performant Web Apps. "O'Reilly Media, Inc.". ISBN 978-1-4919-9719-2. https://www.google.co.jp/books/edition/Vue_js_Up_and_Running/appNDwAAQBAJ?hl=ja&gbpv=0 
  18. ^ Stefanov, Stoyan (2021-11-11) (英語). React: Up & Running: Building Web Applications. "O'Reilly Media, Inc.". ISBN 978-1-4920-5141-1. https://www.google.co.jp/books/edition/React_Up_Running/Z_xNEAAAQBAJ?hl=ja&gbpv=0 
  19. ^ a b c d e f g h i j k l m n o p q r s t u v w x y z aa ab ac ad ae af ag ah ai Allen, Jamie (2017-02-21) (英語). Reactive Design Patterns. Simon and Schuster. ISBN 978-1-63835-405-5. https://www.google.co.jp/books/edition/Reactive_Design_Patterns/xzozEAAAQBAJ?hl=ja&gbpv=0 
  20. ^ Davis, Adam L. (2018-11-29) (英語). Reactive Streams in Java: Concurrency with RxJava, Reactor, and Akka Streams. Apress. ISBN 978-1-4842-4176-9. https://www.google.co.jp/books/edition/Reactive_Streams_in_Java/YKR8DwAAQBAJ?hl=ja&gbpv=1&dq=Reactive+Streams+in+Java&printsec=frontcover 
  21. ^ a b c Larman, Craig (2005) (英語). Applying UML and Patterns: An Introduction to Object-oriented Analysis and Design and Iterative Development. Prentice Hall PTR. ISBN 978-0-13-148906-6. https://www.google.co.jp/books/edition/_/tuxQAAAAMAAJ?hl=ja&sa=X&ved=2ahUKEwibnqWMy5eIAxWbf_UHHWgrBAIQ7_IDegQIExAC 
  22. ^ a b c Horstmann, Cay S. (2006) (英語). Object-Oriented Design & Patterns. Wiley. ISBN 978-0-471-74487-0. https://www.google.co.jp/books/edition/_/GQklAQAAIAAJ?hl=ja&sa=X&ved=2ahUKEwjA2fymy5eIAxUxga8BHWGiMGwQ7_IDegQIHhAC 
  23. ^ a b c d e Goetz, Brian (2006) (英語). Java Concurrency in Practice. Addison-Wesley. ISBN 978-0-321-34960-6. https://www.google.co.jp/books/edition/Java_Concurrency_in_Practice/6LpQAAAAMAAJ?hl=ja&gbpv=1&bsq=Java+Concurrency+in+Practice&dq=Java+Concurrency+in+Practice&printsec=frontcover 
  24. ^ a b c Narkhede, Neha; Shapira, Gwen; Palino, Todd (2017) (英語). Kafka: The Definitive Guide: Real-time Data and Stream Processing at Scale. O'Reilly Media. ISBN 978-1-4919-3616-0. https://www.google.co.jp/books/edition/_/qIjQjgEACAAJ?hl=ja&sa=X&ved=2ahUKEwjeq-ndxpeIAxWoZvUHHdlKOKsQ7_IDegQIFBAD 
  25. ^ Beyer, Betsy; Murphy, Niall Richard; Rensin, David K.; Kawahara, Kent; Thorne, Stephen (2018-07-25) (英語). The Site Reliability Workbook: Practical Ways to Implement SRE. "O'Reilly Media, Inc.". ISBN 978-1-4920-2945-8. https://www.google.co.jp/books/edition/The_Site_Reliability_Workbook/fElmDwAAQBAJ?hl=ja&gbpv=1&dq=The+Site+Reliability+Workbook:+Practical+Ways+to+Implement+SRE&printsec=frontcover