我想要创建一条消息总线,以便可以编写发布者,如下所示:

unit Publisher;

interface

type
  TStuffHasHappenedMessage
             = class( TMessage )
               public
                 Text: string;
                 constructor Create( aText: string );
               end;

  TSomeClass = class
                 procedure DoStuff;
               end;

implementation

constructor TStuffHasHappenedMessage.Create( aText: string );
begin
  Text := aText;
end;

procedure TSomeClass.DoStuff;
begin
  ...
  TMessageBus.Notify( Self, TStuffHasHappenedMessage.Create( 'Some Text' ) );
end;

end.


和一个订户如下:

unit Subscriber;

interface

uses
  Publisher;

TMyClass = class
             procedure MyHandler( Sender: TObject; Message: TStuffHasHappenedMessage );
             constructor Create;
           end

constructor TMyClass.Create;
begin
  TMessageBus.Subscribe( TStuffHasHappenedMessage, MyHandler );
end;

procedure TMyClass.MyHandler( Sender: TObject; Message: TStuffHasHappenedMessage );
begin
  ShowMessage( Message.Text )
end;

end.


我最终希望通过允许通过以下方式调用“ Subcribe”来避免在“ MyHandler”中进行类型转换:
通用类型的任何处理程序:

THandler<T:TMessage> = procedure ( Sender: TObject: Message: T );


我无法弄清楚如何声明和实现“ TMessageBus.Subscribe”以支持此功能

最佳答案

您可以检查如何实现标准TMessageManager。我不认为您当前想在Delphi中实现什么目标,因为您不能将不同类的对象存储在列表中,然后在提取时不将其转换为适当的类而进行提取。

type
  TStringMessage = TMessage<string>;

procedure TForm1.Button9Click(Sender: TObject);
begin
  TMessageManager.DefaultManager.SubscribeToMessage(TStringMessage,
    procedure(const Sender: TObject; const M: TMessage)
  begin
    ShowMessage(TStringMessage(M).Value);
  end);

  TMessageManager.DefaultManager.SendMessage(Self, TStringMessage.Create('test'), True);
end;


更新资料

实际上,在一些RTTI帮助下,我认为可以做一些接近您想要的事情。

在下面的单元中,您可以编写以下内容

type
  TTestMessage = class(TMessage)
    Test: string;
    constructor Create(const ATest: string);
  end;

constructor TTestMessage.Create(const ATest: string);
begin
  Test := ATest;
end;

procedure HandleMessage(const ASender: TObject; const AMyTestMessage: TTestMessage);
begin
  ShowMessage(AMyTestMessage.Test);
end;

procedure TMainForm.Button6Click(Sender: TObject);
begin
  TPublisher<TTestMessage>.Subscribe(HandleMessage);
  MessageBus.SendMessage(Self, TTestMessage.Create('test'));
end;


这是发布者,请注意该文件必须命名为UPublisher.pas

unit UPublisher;

interface

uses System.Messaging;

type
  TPublisherBase = class
  protected
    procedure SendMessageM(const ASender: TObject; const AMessage: TMessage); virtual; abstract;
  end;

  TPublisherBaseClass = class of TPublisherBase;

  TPublisher<T: class> = class(TPublisherBase)
  private
    type
      THandler = procedure(const Sender: TObject; const AMessage: T);
  private
    class var FHandlers: TArray<THandler>;
    class var FPublisher: TPublisher<T>;
  protected
    procedure SendMessageM(const ASender: TObject; const AMessage: TMessage); override;
    class procedure SendMessage(const ASender: TObject; const AMessage: T);
  public
    class constructor Create;
    class destructor Destroy;
    class procedure Subscribe(const AHandler: THandler);
  end;

  TMessageBus = class
  strict private
    FPublishers: TArray<TPublisherBase>;
  private
    procedure RegisterPublisher(const APublisher: TPublisherBase);
  public
    procedure SendMessage(const ASender: TObject; const AMessage: TMessage);
    constructor Create;
  end;

var
  MessageBus: TMessageBus;

implementation

constructor TMessageBus.Create;
begin
  FPublishers := [];
end;

procedure TMessageBus.RegisterPublisher(const APublisher: TPublisherBase);
begin
  FPublishers := FPublishers + [APublisher];
end;

procedure TMessageBus.SendMessage(const ASender: TObject; const AMessage: TMessage);
var
  Publisher: TPublisherBase;
  PublisherType: string;
begin
  PublisherType := 'UPublisher.TPublisher<' + AMessage.QualifiedClassName + '>';

  for Publisher in FPublishers do
  begin
    if Publisher.QualifiedClassName = PublisherType then
    begin
      Publisher.SendMessageM(ASender, AMessage);
    end;
  end;
end;

class constructor TPublisher<T>.Create;
begin
  FHandlers := [];
  FPublisher := TPublisher<T>.Create;
  MessageBus.RegisterPublisher(FPublisher);
end;

class destructor TPublisher<T>.Destroy;
begin
  FPublisher.Free;
end;

class procedure TPublisher<T>.Subscribe(const AHandler: THandler);
begin
  FHandlers := FHandlers + [@AHandler];
end;

procedure TPublisher<T>.SendMessageM(const ASender: TObject; const AMessage: TMessage);
begin
  SendMessage(ASender, T(AMessage));
end;

class procedure TPublisher<T>.SendMessage(const ASender: TObject; const AMessage: T);
var
  Handler: THandler;
begin
  for Handler in FPublisher.FHandlers do
  begin
    Handler(ASender, AMessage);
  end;
end;

initialization
  MessageBus := TMessageBus.Create;
finalization
  MessageBus.Free;
end.

09-30 16:28