Я ищу ссылки в стиле Википедии на проекты и реализации облегченных механизмов публикации-подписки, которые действительно работают. Я буду обновлять вопрос в соответствии с ответами и комментариями, а также своими собственными исследованиями.
Я исследовал свои книги и Интернет для работы, проделанной в Python и Delphi для публикации / подписки, и не был доволен результатами. Проекты основывались на сигнатурах функций или растровых изображениях или слотах для фильтрации сообщений или определения того, что следует доставлять кому, и были либо слишком жесткими (привязаны к серверу обмена сообщениями), либо слишком разнородными (каждый может подписаться на что угодно ).
Я не хочу писать свои собственные. Я хочу найти что-то, что уже хорошо спроектировано, обсуждено и проверено на практике.
Сегодня я реализовал дизайн в Delphi Pascal (потому что Delphi был тем, что мне было нужно сначала). Диспетчеризация по типу аргумента, как это делает API, не является оригинальной идеей (объясняется шаблон Design Patterns Visitor
), и я думаю, что я видел нечто подобное раньше (но я не помнишь где; талигент?). Суть в том, что подписка, фильтрация и рассылка осуществляется по системе типов.
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. Я изменил названия клавиш, потому что signal и message уже слишком перегружены. В отличие от реализации 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())
Вот причины моего поиска:
- Я прошёл несколько тысяч строк кода Delphi, которые мне нужно поддерживать. Они используют шаблон Observer для разъединения MVC, но все по-прежнему очень взаимосвязано, потому что зависимости между наблюдателями и субъектами слишком явные.
- Я изучал PyQt4, и он убил бы меня, если бы мне пришлось щелкать, щелкать, щелкать в Qt4Designer для каждого события, которое я хочу достичь в значимом месте назначения.
- Тем не менее, в другом приложении для работы с личными данными мне нужно абстрагироваться от передачи и обработки событий, поскольку постоянство и пользовательский интерфейс будут различаться в зависимости от платформы, и должен быть полностью независимым.
Ссылки
Найден сам, и другие должны идти сюда
- PybubSub использует строки для топиков и сигнатуры методов (первый сигнал определяет сигнатуру).
- Статья в блоге FinalBuilder сообщает, что они успешно использовали систему с целочисленными структурами в качестве полезных нагрузок, в качестве сообщений и целочисленных масок для фильтрации.
- PyDispatcher имеет минимальную документацию.
- D-Bus был принят, среди прочего, в проектах Gnome и KDE. Доступно связывание Python .