编辑:找到了一种使这个过程更简单的方法(如果您喜欢这个答案,请投票支持;我花了很长时间才搞定)。
请注意,它使用了新的Rtti
单元,因此仅适用于Delphi 2010及以上版本(我在Delphi XE中开发了这个,但尚未在Delphi 2010中验证)。
对于Supports
,您需要存储一些IID GUID与您的接口以及查询它们的方法。
由于您希望将其与泛型一起使用,因此要能够从接口类型而不是接口引用中查询IID GUID(正如Hallvard Vassbotn在2006年通过hack所示)。
Delphi 2010中引入的新RTTI正好可以做到这一点:
unit RttiUnit;
interface
type
TRtti = record
class function GetInterfaceIID<T: IInterface>(var IID: TGUID): Boolean; static;
end;
implementation
uses
TypInfo,
Rtti;
class function TRtti.GetInterfaceIID<T>(var IID: TGUID): Boolean;
var
TypeInfoOfT: PTypeInfo;
RttiContext: TRttiContext;
RttiInterfaceType: TRttiInterfaceType;
RttiType: TRttiType;
begin
TypeInfoOfT := TypeInfo(T);
RttiContext := TRttiContext.Create();
RttiType := RttiContext.GetType(TypeInfoOfT);
if RttiType is TRttiInterfaceType then
begin
RttiInterfaceType := RttiType as TRttiInterfaceType;
IID := RttiInterfaceType.GUID;
Result := True;
end
else
Result := False;
end;
end.
现在是更改后的代码,我稍微重新排列了一下,并将其分散到更多单元中以保持概览。
ClassicMessageSubscriberUnit:具有非泛型接口IMessage
和ISubscriber
(它们都派生自IImplementedWithClass
,这使得记录事务更加容易。
unit ClassicMessageSubscriberUnit;
interface
type
IImplementedWithClass = interface(IInterface)
function ToString: string;
end;
IMessage = interface(IImplementedWithClass)
['{B1794F44-F6EE-4E7B-849A-995F05897E1C}']
end;
ISubscriber = interface(IImplementedWithClass)
['{D655967E-90C6-4613-92C5-1E5B53619EE0}']
end;
implementation
end.
GenericSubscriberOfUnit:包含通用的ISubscriberOf
接口,该接口派生自通用的ISupporterOf
接口和一个名为TSupporterOf
的通用基础实现:
unit GenericSubscriberOfUnit;
interface
uses
ClassicMessageSubscriberUnit;
type
ISupporterOf<T: IMessage> = interface(ISubscriber)
['{0905B3EB-B17E-4AD2-98E2-16F05D19484C}']
function Supports(const Message: T): Boolean;
end;
ISubscriberOf<T: IMessage> = interface(ISupporterOf<T>)
['{6FD82B1D-61C6-4572-BA7D-D70DA9A73285}']
procedure Consume(const Message: T);
end;
type
TSupporterOf<T: IMessage> = class(TInterfacedObject, ISubscriber, ISupporterOf<T>)
function Supports(const Message: T): Boolean;
end;
implementation
uses
SysUtils,
RttiUnit;
function TSupporterOf<T>.Supports(const Message: T): Boolean;
var
IID: TGUID;
begin
if TRtti.GetInterfaceIID<T>(IID) then
Result := SysUtils.Supports(Message, IID)
else
Result := False;
end;
end.
MessageServiceUnit:现在仅包含TMessageService
、一些类型别名和实际代码,用于管理列表以便我可以进行实际测试。
unit MessageServiceUnit;
interface
uses
Generics.Collections,
ClassicMessageSubscriberUnit,
GenericSubscriberOfUnit;
type
ISubscriberOfIMessage = ISubscriberOf<IMessage>;
TListISubscriber = TList<ISubscriber>;
TMessageService = class
private
FSubscribers: TListISubscriber;
strict protected
procedure Consume(const SubscriberOf: ISubscriberOfIMessage; const Message: IMessage); virtual;
public
constructor Create;
destructor Destroy; override;
procedure SendMessage(const Message: IMessage);
procedure Subscribe(const Subscriber: ISubscriber);
procedure Unsubscribe(const Subscriber: ISubscriber);
end;
implementation
uses
SysUtils;
constructor TMessageService.Create;
begin
inherited Create();
FSubscribers := TListISubscriber.Create();
end;
destructor TMessageService.Destroy;
begin
FreeAndNil(FSubscribers);
inherited Destroy();
end;
procedure TMessageService.SendMessage(const Message: IMessage);
var
LocalMessage: IMessage;
lSubscriber: ISubscriber;
lSubscriberOf: ISubscriberOf<IMessage>;
begin
for lSubscriber in FSubscribers do
begin
LocalMessage := Message;
if Supports(lSubscriber, ISubscriberOf<IMessage>, lSubscriberOf) then
if lSubscriberOf.Supports(LocalMessage) then
Consume(lSubscriberOf, LocalMessage);
end;
end;
procedure TMessageService.Subscribe(const Subscriber: ISubscriber);
begin
FSubscribers.Add(Subscriber);
end;
procedure TMessageService.Unsubscribe(const Subscriber: ISubscriber);
begin
FSubscribers.Remove(Subscriber);
end;
procedure TMessageService.Consume(const SubscriberOf: ISubscriberOfIMessage; const Message: IMessage);
begin
SubscriberOf.Consume(Message);
end;
end.
最后,我使用了一个单元来测试所有内容(它使用了位于http://bo.codeplex.com的bo-library):
unit GenericPublishSubscribeMainFormUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, LoggerInterfaceUnit, MessageServiceUnit,
MessageSubscribersUnit, ClassicMessageSubscriberUnit;
type
TGenericPublishSubscribeMainForm = class(TForm)
TestPublisherButton: TButton;
LogMemo: TMemo;
procedure TestPublisherButtonClick(Sender: TObject);
strict private
FLogger: ILogger;
strict protected
function GetLogger: ILogger;
property Logger: ILogger read GetLogger;
public
destructor Destroy; override;
end;
type
TLoggingMessageService = class(TMessageService)
strict private
FLogger: ILogger;
strict protected
procedure Consume(const SubscriberOf: ISubscriberOfIMessage; const Message: IMessage); override;
public
constructor Create(const Logger: ILogger);
property Logger: ILogger read FLogger;
end;
var
GenericPublishSubscribeMainForm: TGenericPublishSubscribeMainForm;
implementation
uses
LoggerUnit,
OutputDebugViewLoggerUnit,
LoggersUnit,
MessagesUnit;
{$R *.dfm}
destructor TGenericPublishSubscribeMainForm.Destroy;
begin
inherited Destroy;
FLogger := nil;
end;
function TGenericPublishSubscribeMainForm.GetLogger: ILogger;
begin
if not Assigned(FLogger) then
FLogger := TTeeLogger.Create([
TOutputDebugViewLogger.Create(),
TStringsLogger.Create(LogMemo.Lines)
]);
Result := FLogger;
end;
procedure TGenericPublishSubscribeMainForm.TestPublisherButtonClick(Sender: TObject);
var
LoggingMessageService: TLoggingMessageService;
begin
LoggingMessageService := TLoggingMessageService.Create(Logger);
try
LoggingMessageService.Subscribe(TMySubscriber.Create() as ISubscriber);
LoggingMessageService.Subscribe(TMyOtherSubscriber.Create() as ISubscriber);
LoggingMessageService.SendMessage(TMyMessage.Create());
LoggingMessageService.SendMessage(TMyOtherMessage.Create());
finally
LoggingMessageService.Free;
end;
end;
constructor TLoggingMessageService.Create(const Logger: ILogger);
begin
inherited Create();
FLogger := Logger;
end;
procedure TLoggingMessageService.Consume(const SubscriberOf: ISubscriberOfIMessage; const Message: IMessage);
var
MessageImplementedWithClass: IImplementedWithClass;
MessageString: string;
SubscribeImplementedWithClass: IImplementedWithClass;
SubscriberOfString: string;
begin
SubscribeImplementedWithClass := SubscriberOf;
MessageImplementedWithClass := Message;
SubscriberOfString := SubscribeImplementedWithClass.ToString;
MessageString := MessageImplementedWithClass.ToString;
Logger.Log('Consume(SubscriberOf: %s, Message:%s);',
[SubscriberOfString, MessageString]);
inherited Consume(SubscriberOf, Message);
end;
end.
旧解决方案:
这可能可以解决问题,但我仍然觉得解决方案有点复杂。
MessageServiceUnit:ISubscriberOf
现在具有GUID
和Supports
方法来检查是否实际支持IMessage
。
unit MessageServiceUnit;
interface
uses
Generics.Collections;
type
IMessage = interface(IInterface)
['{B1794F44-F6EE-4E7B-849A-995F05897E1C}']
end;
ISubscriber = interface(IInterface)
['{D655967E-90C6-4613-92C5-1E5B53619EE0}']
end;
ISubscriberOf<T: IMessage> = interface(ISubscriber)
['{6FD82B1D-61C6-4572-BA7D-D70DA9A73285}']
procedure Consume(const Message: T);
function Supports(const Message: T): Boolean;
end;
TMessageService = class
private
FSubscribers: TList<ISubscriber>;
public
constructor Create;
destructor Destroy; override;
procedure SendMessage(const Message: IMessage);
procedure Subscribe(const Subscriber: ISubscriber);
procedure Unsubscribe(const Subscriber: ISubscriber);
end;
implementation
uses
SysUtils;
constructor TMessageService.Create;
begin
inherited Create();
end;
destructor TMessageService.Destroy;
begin
inherited Destroy();
end;
procedure TMessageService.SendMessage(const Message: IMessage);
var
lSubscriber: ISubscriber;
lSubscriberOf: ISubscriberOf<IMessage>;
begin
for lSubscriber in FSubscribers do
begin
if Supports(lSubscriber, ISubscriberOf<IMessage>, lSubscriberOf) then
if lSubscriberOf.Supports(Message) then
lSubscriberOf.Consume(Message);
end;
end;
procedure TMessageService.Subscribe(const Subscriber: ISubscriber);
begin
FSubscribers.Add(Subscriber);
end;
procedure TMessageService.Unsubscribe(const Subscriber: ISubscriber);
begin
FSubscribers.Remove(Subscriber);
end;
end.
MessagesUnit: 每个消息都有一个带有GUID
的interface
,以便Supports
可以检查GUID
。
unit MessagesUnit;
interface
uses
MessageServiceUnit;
type
IMyMessage = interface(IMessage)
['{84B42EC8-CAC0-44B4-97A8-05AE5B636236}']
end;
TMyMessage = class(TInterfacedObject, IMessage, IMyMessage);
IMyOtherMessage = interface(IMessage)
['{AB323765-FF7B-4852-91AA-B7ECC1845B41}']
end;
TMyOtherMessage = class(TInterfacedObject, IMessage, IMyOtherMessage);
implementation
end.
MessageSubscribersUnit: 所有的订阅者都有一个Supports
方法,用于检查正确的GUID
。
unit MessageSubscribersUnit;
interface
uses
MessagesUnit, MessageServiceUnit;
type
TMySubscriber = class(TInterfacedObject, ISubscriberOf<IMyMessage>)
procedure Consume(const Message: IMyMessage);
function Supports(const Message: IMyMessage): Boolean;
end;
TMyOtherSubscriber = class(TInterfacedObject, ISubscriberOf<IMyOtherMessage>)
procedure Consume(const Message: IMyOtherMessage);
function Supports(const Message: IMyOtherMessage): Boolean;
end;
implementation
uses
SysUtils;
procedure TMySubscriber.Consume(const Message: IMyMessage);
begin
end;
function TMySubscriber.Supports(const Message: IMyMessage): Boolean;
begin
Result := SysUtils.Supports(Message, IMyMessage);
end;
procedure TMyOtherSubscriber.Consume(const Message: IMyOtherMessage);
begin
end;
function TMyOtherSubscriber.Supports(const Message: IMyOtherMessage): Boolean;
begin
Result := SysUtils.Supports(Message, IMyOtherMessage);
end;
end.
MessagesUnit: 包含具体的消息(包括接口和类类型),其中包含IID GUID以使它们与Supports
区分开来。
unit MessagesUnit;
interface
uses
MessageServiceUnit,
ClassicMessageSubscriberUnit;
type
IMyMessage = interface(IMessage)
['{84B42EC8-CAC0-44B4-97A8-05AE5B636236}']
end;
TMyMessage = class(TInterfacedObject, IMessage, IMyMessage);
IMyOtherMessage = interface(IMessage)
['{AB323765-FF7B-4852-91AA-B7ECC1845B41}']
end;
TMyOtherMessage = class(TInterfacedObject, IMessage, IMyOtherMessage);
implementation
end.
MessageSubscribersUnit:包含特定的订阅者(接口和类类型),它们现在不再需要 Supports
方法:它们只包含 Consume
方法。
unit MessageSubscribersUnit;
interface
uses
MessagesUnit,
MessageServiceUnit,
GenericSubscriberOfUnit,
ClassicMessageSubscriberUnit;
type
TMySubscriber = class(TSupporterOf<IMyMessage>, ISubscriber, ISubscriberOf<IMyMessage>)
procedure Consume(const Message: IMyMessage);
end;
TMyOtherSubscriber = class(TSupporterOf<IMyOtherMessage>, ISubscriber, ISubscriberOf<IMyOtherMessage>)
procedure Consume(const Message: IMyOtherMessage);
end;
implementation
uses
SysUtils;
procedure TMySubscriber.Consume(const Message: IMyMessage);
begin
end;
procedure TMyOtherSubscriber.Consume(const Message: IMyOtherMessage);
begin
end;
end.
--jeroen
TMessageService.SendMessage
将消息发送给每个订阅者,让订阅者的Consume
方法决定是否处理它或忽略它,这样不是更容易吗? - Kenneth Cochran