Я делаю eventPublisher на основе примера документации Spring4d.
Разница в том, что подписчики должны явно подписываться на события.
Я хочу инициировать их процедуру Handle в зависимости от того, реализуют ли они интерфейс IEventHandler<TEventType>
.
Когда входящее событие публикуется, я нахожу ссылку типа IEventHandler<TEventType>
, используя имя класса события и TType.FindType('IEventHandler<TEvent1>')
Spring4d.
Затем я перебираю своих подписчиков (объекты, реализующие интерфейс IEventHandler) и проверяю, поддерживает ли он, например, тип IEventHandler.
Проблема в том, что метод Supports возвращает true, даже если подписчик не реализует интерфейс.
Кроме того, я попытался перечислить интерфейсы, скажем, типа TMyEventHandler2
. Он содержит IEventHandler<TEvent2>
??
Я полагаю, что это связано с ограничением, когда IEventHandler<TEvent2>
и IEventHandler<TEvent1>
используют один и тот же GUID.
Есть ли обходной путь для этого?
Используя эти классы и интерфейсы:
TEvent1 = class(TObject)
end;
TEvent2 = class(TObject)
end;
IEventHandler = interface(IInvokable)
[guid]
procedure Handle(aEvent : TObject);
end;
IEventHandler<T : class> = interface(IEventHandler)
[guid]
procedure Handle(aEvent : T);
end;
TMyEventHandler1 = class(TObject, IEventHandler, IEventHandler<TEvent1>)
public
procedure Handle(AEvent : TObject); overload;
procedure Handle(AEvent : TEvent1); overload;
end;
TMyEventHandler2 = class(TObject, IEventHandler, IEventHandler<TEvent2>)
public
procedure Handle(AEvent : TObject); overload;
procedure Handle(AEvent : TEvent2); overload;
end;
TEventPublisher = class(TObject)
public
fSubscribers : IList<TValue>;
procedure Subscribe(aSubscriber : TValue); // Simply adds the subscriber to the list of subscribers
procedure Publish(aEvent : TObject); // Publishes an event to the subscribers
end;
procedure TEventPublisher.Publish(const event: TObject; ownsObject: Boolean = True);
const
IEventSubscriberName = 'IEventSubscriber<*>';
var
consumerTypeName: string;
consumerType : TRttiType;
intfType : TRttiInterfaceType;
subscriber : TValue;
subscribed : IInterface;
lEventSubscriber: IEventSubscriber;
lIntfs : IReadOnlyList<TRttiInterfaceType>;
begin
consumerTypeName := StringReplace(IEventSubscriberName, '*', GetQualifiedClassName(event), []);
consumerType := TType.FindType(consumerTypeName);
intfType := consumerType as TRttiInterfaceType;
for subscriber in fSubscribers do
begin
lIntfs := TType.GetType(subscriber.AsObject.ClassInfo).GetInterfaces();
// lIntfs for TMyEventHandler2 containts IEventHandler<TEvent1> ???
if Supports(subscriber.AsObject, intfType.GUID, subscribed) then
if Supports(subscriber.AsObject, IEventSubscriber, lEventSubscriber) then
begin
intfType.GetMethod('Handle').Invoke(TValue.From(@subscribed, intfType.Handle), [event])
end;
end;
if ownsObject then
event.Free;
end;
lEventPublisher := TEventPublisher.Create;
lEventPublisher.Subscribe(TMyEventHandler1.Create);
lEventPublisher.Subscribe(TMyEventHandler2.Create);
lEventPublisher.Publish(TEvent1.Create); // Will both trigger TMyEventHandler1.Handle and TMyEventHandler2.Handle. Why ??