2

実際に機能する軽量のパブリッシュ/サブスクライブ メカニズムの設計と実装について、Wikipedia スタイルのリファレンスを探しています。回答とコメント、および私自身の調査に従って、質問を更新します。

私は自分の本や Web を調べて Python で、Delphi で行ったパブリッシュ/サブスクライブを調べましたが、満足のいく結果にはなりませんでした。この設計は、メッセージのフィルター処理や誰に何を配信するかを決定するために、関数シグネチャ、ビットマップ、またはスロットに依存しており、制限が多すぎる (メッセージング サーバーにバインドされている) か、無差別すぎる (誰もが何にでもサブスクライブできる) かのいずれかでした。

自分で書きたくない。私は、すでにうまく設計され、議論され、現場で証明されているものを見つけたいと思っています.

今日、私は Delphi Pascal で設計を実装しました (Delphi が最初に必要だったからです)。この API のように、引数の型でディスパッチすることは独創的なアイデアではなく (デザイン パターン Visitorパターンで説明されています)、以前にこのようなものを見たことがあると思います (しかし、場所は覚えていません; Taligent?)。その核心は、サブスクリプション、フィルタリング、およびディスパッチが型システム上にあるということです。

unit JalSignals;
//  A publish/subscribe mechanism.    
//  1. Signal payloads are objects, and their class is their signal type.
//  2. Free is called on the payloads after they have been delivered.    
//  3. Members subscribe by providing a callback method (of object).
//  4. Members may subscribe with the same method to different types of signals.
//  5. A member subscribes to a type, which means that all signals
//     with payloads of that class or any of its subclasses will be delivered
//     to the callback, with one important exception    
//  6. A forum breaks the general class hierarchy into independent branches.    
//     A signal will not be delivered to members subscribed to classes that    
//     are not in the branch.    
//  7. This is a GPL v3 design.
interface
uses
  SysUtils;
type
  TSignal = TObject;
  TSignalType = TClass;
  TSignalAction = (soGo, soStop);
  TCallback = function(signal :TSignal) :TSignalAction of object;

  procedure signal(payload: TSignal);

  procedure subscribe(  callback :TCallback; atype :TSignalType);
  procedure unsubscribe(callback :TCallback; atype :TSignalType = nil); overload;
  procedure unsubscribe(obj      :TObject;   atype :TSignalType = nil); overload;

  procedure openForum( atype :TSignalType);
  procedure closeForum(atype :TSignalType);

上記の「コールバック」は、Python のバインドされたメソッドのようなものです。

Delphi 実装の完全なソース コードは次のとおりです。

これは Python での実装です。シグナルメッセージはすでにオーバーロードされているため、キー名を変更しました。Delphi の実装とは異なり、例外を含む結果が収集され、リストでシグナル送信者に返されます。

"""
  A publish/subscribe mechanism.

  1. Signal payloads are objects, and their class is their signal type.
  2. Free is called on the payloads after they have been delivered.
  3. Members subscribe by providing a callback method (of object).
  4. Members may subscribe with the same method to different types of signals.
  5. A member subscribes to a type, which means that all signals
     with payloads of that class or any of its subclasses will be delivered
     to the callback, with one important exception:
  6. A forum breaks the general class hierarchy into independent branches.
     A signal will not be delivered to members subscribed to classes that
     are not in the branch.
"""

__all__ = ['open_forum', 'close_forum', 'announce',
           'subscribe', 'unsubscribe'
           ]

def _is_type(atype):
    return issubclass(atype, object)

class Sub(object):
    def __init__(self, callback, atype):
        assert callable(callback)
        assert issubclass(atype, object)
        self.atype = atype
        self.callback = callback

__forums = set()
__subscriptions = []

def open_forum(forum):
    assert issubclass(forum, object)
    __forums.add(forum)

def close_forum(forum):
    __forums.remove(forum)

def subscribe(callback, atype):
    __subscriptions.append(Sub(callback, atype))

def unsubscribe(callback, atype=None):
    for i, sub in enumerate(__subscriptions):
        if sub.callback is not callback:
            continue
        if atype is None or issubclass(sub.atype, atype):
            del __subscriptions[i]

def _boundary(atype):
    assert _is_type(atype)
    lower = object
    for f in __forums:
        if (issubclass(atype, f)
            and issubclass(f, lower)):
            lower = f
    return lower

def _receivers(news):
    bound = _boundary(type(news))
    for sub in __subscriptions:
        if not isinstance(news, sub.atype):
            continue
        if not issubclass(sub.atype, bound):
            continue
        yield sub

def announce(news):
    replies = []
    for sub in _receivers(news):
        try:
            reply = sub.callback(news)
            replies.append(reply)
        except Exception as e:
            replies.append(e)
    return replies

if __name__ == '__main__':
    i = 0
    class A(object):
        def __init__(self):
            global i
            self.msg = type(self).__name__ + str(i)
            i += 1

    class B(A): pass
    class C(B): pass

    assert _is_type(A)
    assert _is_type(B)
    assert _is_type(C)

    assert issubclass(B, A)
    assert issubclass(C, B)

    def makeHandler(atype):
        def handler(s):
            assert isinstance(s, atype)
            return 'handler' + atype.__name__ + ' got ' + s.msg
        return handler

    handleA = makeHandler(A)
    handleB = makeHandler(B)
    handleC = makeHandler(C)

    def failer(s):
        raise Exception, 'failed on' + s.msg

    assert callable(handleA) and callable(handleB) and callable(handleC)

    subscribe(handleA, A)
    subscribe(handleB, B)
    subscribe(handleC, C)
    subscribe(failer, A)

    assert _boundary(A) is object
    assert _boundary(B) is object
    assert _boundary(C) is object

    print announce(A())
    print announce(B())
    print announce(C())

    print
    open_forum(B)

    assert _boundary(A) is object
    assert _boundary(B) is B
    assert _boundary(C) is B
    assert issubclass(B, B)

    print announce(A())
    print announce(B())
    print announce(C())

    print
    close_forum(B)
    print announce(A())
    print announce(B())
    print announce(C())

これらは私の検索の理由です:

  1. 私は維持しなければならない数千行の Delphi コードを調べてきました。彼らはMVC デカップリングにオブザーバーパターンを使用していますが、オブザーバーとサブジェクトの間の依存関係が明示的すぎるため、すべてが非常に結合されています。
  2. 私は PyQt4 を学んでいますが、意味のある目的地に到着したいすべてのイベントに対して Qt4Designer でクリック-クリック-クリックする必要がある場合、それは私を殺します。
  3. さらに、別の個人データ アプリケーションでは、イベントの受け渡しと処理を抽象化する必要があります。これは、持続性と UI がプラットフォームによって異なり、完全に独立している必要があるためです。

参考文献

自分で見つけて、他の人はここに行くべきです

  • PybubSubは topycs とメソッド シグネチャに文字列を使用します (最初のシグナルがシグネチャを定義します)。
  • FinalBuilderのブログの記事では、ペイロード、メッセージ、およびフィルタリング用の整数マスクとして整数構造を持つシステムを使用して成功したと報告しています。
  • PyDispatcherのドキュメントは最小限です。
  • D-Busは Gnome や KDE プロジェクトなどで採用されています。Python バインディングが利用可能です。
4

2 に答える 2

2

DDS を試すこともできます。Data Distribution Service は、パブリッシュ/サブスクライブ セマンティクスを使用した通信パターンの完全な標準です。

于 2013-01-03T19:49:23.373 に答える
2

エンタープライズ統合パターンを試す必要があります。これは、プロセス間メッセージの受け渡しに重点を置いていますが、パブリッシュ/サブスクライブに非常に詳細な処理を提供します。

于 2011-01-21T18:03:20.373 に答える