delphi - 是否可以为 Delphi 创建一个通用调解器来处理通用命令
问题描述
我必须首先承认我来自 .Net 世界,目前正在重新学习 Delphi (XE 10.x)(从高中开始 - 许多年前)。在 .Net 中,MediatR或MassTransit等库可以很好地处理中介模式。然而,我发现很少有库支持 Delphi 中中介者模式的动态(或半动态)实现。在不去扫描正在执行的 Rtti 信息的花哨级别的情况下,我想创建一个简单的中介,我可以在其中通过 Request 注册一个 CommandHandler,然后得到响应。这可能吗?
这是迄今为止我制作的一些示例代码 - 但我只是陷入了如何动态创建对象以及我的方法是否合理的问题上。
在检查代码之前,我并没有坚持使用 TDictionary<string, string> 来注册类型,但是,我对 Rtti 的了解有限,很难确定它应该使用 TClass 还是 TRttiTypes。如果其中任何一个有帮助,我将不胜感激。
// interface
uses
System.Generics.Collections;
type
TUnit = record
end;
IRequest<TResponse> = interface
end;
IRequest = interface(IRequest<TUnit>)
end;
IRequestHandler<TResponse; TRequest: IRequest<IResponse>> = interface(IInvokable)
function Handle(ARequest: TRequest): TResponse;
end;
IRequestHandler<TRequest: IRequest<TUnit>> = interface(IRequestHandler<TUnit, TRequest>)
end;
TMediator = class
private
FRequestHandlers: TDictionary<string, string>;
public
constructor Create;
destructor Destroy; override;
procedure RegisterHandler(AHandlerClass, ARequestClass: TClass);
function Send<TResponse, TRequest>(ARequest: TRequest): TResponse;
end;
// implementation
constructor TMediator.Create;
begin
Self.FRequestHandlers := TDictionary<string, string>.Create;
end;
destructor TMediator.Destroy;
begin
Self.FRequestHandlers.Free;
inherited;
end;
procedure TMediator.RegisterHandler(AHandlerClass, ARequestClass: TClass);
var
LTempRequestClass : string;
rContext : TRttiContext;
rType : TRttiType;
begin
if Self.FRequestHandlers.TryGetValue(ARequestClass.QualifiedClassName, LTempRequestClass) then
exit;
{ I would like to add some error checking functionality to prevent classes
that do not implement IRequest or IRequest<> from being added here. }
Self.FRequestHandlers.Add(ARequestClass.QualifiedClassName, AHandlerClass.QualifiedClassName);
end;
function TMediator.Send<TResponse, TRequest>(ARequest: TRequest): TResponse;
var
LRequestHandlerClassName: string;
LRequestHandler : IRequestHandler<TResponse, TRequest>;
begin
if not Self.FRequestHandlers.TryGetValue(ARequest.QualifiedClassName, LRequestHandlerClassName) then
raise Exception.Create('Handler class not registered with this mediator.');
{ Not sure what to do here to get the LRequestHandler - I'm also using Spring4d,
so I considered using the QualifiedClassName as a way to resolve classes
registered in the TContainer }
Result := LRequestHandler.Handle(ARequest);
end;
我对此的预期用法是:
注意:下面的编辑 - 我希望能够从单个主持人注册和调用任何实现 IRequest 或 IRequest<> 的命令。
// interface
type
TMyResponse = class
private
FFoo: string;
public
property Foo: string read FFoo write FFoo;
end;
TMyResponse2 = class
private
FFoo2: string;
public
property Foo2: string read FFoo2 write FFoo2;
end;
TMyRequest = class(TInterfacedObject, IRequest<TMyResponse>)
private
FBar: string;
public
property Bar: string read FBar write FBar;
end;
TMyRequest2 = class(TInterfacedObject, IRequest<TMyResponse2>)
private
FBar2: string;
public
property Bar2: string read FBar2 write FBar2;
end;
TMyRequestHandler = class(TInterfacedObject, IRequestHandler<TMyResponse, TMyRequest>)
public
function Handle(ARequest: TMyRequest): TMyResponse;
end;
TMyRequestHandler2 = class(TInterfacedObject, IRequestHandler<TMyResponse2, TMyRequest2>)
public
function Handle(ARequest: TMyRequest2): TMyResponse2;
end;
// implementation
var
AMediator: TMediator;
ARequest: TMyRequest;
ARequest2: TMyRequest2;
AResponse: TMyResponse;
AResponse2: TMyResponse2;
begin
AMediator := TMediator.Create;
ARequest := TMyRequest.Create;
ARequest2 := TMyRequest2.Create;
try
ARequest.Bar := 'something';
ARequest2.Bar2 := 'else';
// Not sure how I would get these either - seems best to use the qualified class name
AMediator.Register(TMyRequestHandler.QualifiedClassName, TMyRequest.QualifiedClassName);
AMediator.Register(TMyRequestHandler2.QualifiedClassName, TMyRequest2.QualifiedClassName);
AResponse := AMediator.Send(ARequest);
AResponse2 := AMediator.Send(ARequest2);
// Do something with this value
finally
AResponse2.Free;
AResponse.Free;
ARequest2.Free;
ARequest.Free;
AMediator.Free;
end;
end.
解决方案
所以,我似乎走错了路,多亏了J……他让我重新思考我在做什么。总之,我试图让一些东西充当依赖注入层,以便能够根据给定的“请求”动态运行“处理程序”。最后,似乎简单的解决方案是调用我已经用来执行该功能的 Spring4d DI 层。我仍然觉得有一些相当紧密的耦合,但我目前对结果感到满意。这是代码:
CQRSU.pas
unit CQRSU;
interface
uses
System.Generics.Collections,
Spring.Container;
type
TUnit = record
end;
IBaseRequest = interface(IInvokable)
['GUID']
end;
IRequest<TResponse> = interface(IBaseRequest)
['GUID']
end;
IRequest = interface(IRequest<TUnit>)
['GUID']
end;
IRequestHandler<TResponse; TRequest: IRequest<TResponse>> = interface(IInvokable)
['GUID']
function Handle(ARequest: TRequest): TResponse;
end;
IRequestHandler<TRequest: IRequest<TUnit>> = interface(IRequestHandler<TUnit, TRequest>)
['GUID']
end;
implementation
end.
服务U.pas
unit ServicesU;
interface
uses
CQRSU;
type
TMyResponse = class
private
FMyResult: string;
public
property MyResult: string read FMyResult write FMyResult;
end;
TMyRequest = class(TInterfacedObject, IRequest<TMyResponse>)
private
FMyParameter: string;
public
property MyParameter: string read FMyParameter write FMyParameter;
end;
TMyRequestHandler = class(TInterfacedObject, IRequestHandler<TMyResponse, TMyRequest>)
public
function Handle(ARequest: TMyRequest): TMyResponse;
end;
implementation
{ TMyRequestHandler }
function TMyRequestHandler.Handle(ARequest: TMyRequest): TMyResponse;
begin
Result := TMyResponse.Create;
Result.MyResult := ARequest.MyParameter + ' Processed';
end;
end.
测试CQRS.dpr
program TestCQRS;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Spring.Container,
System.SysUtils,
CQRSU in 'CQRSU.pas',
ServicesU in 'ServicesU.pas';
var
LContainer: TContainer;
LMyRequestHandler: IRequestHandler<TMyResponse, TMyRequest>;
LRequest: TMyRequest;
LResponse: TMyResponse;
begin
LContainer := TContainer.Create;
try
LRequest := TMyRequest.Create;
LRequest.MyParameter := 'Hello there!';
try
LContainer.RegisterType<TMyRequestHandler>.Implements<IRequestHandler<TMyResponse, TMyRequest>>;
LContainer.Build;
LMyRequestHandler := LContainer.Resolve<IRequestHandler<TMyResponse, TMyRequest>>;
LResponse := LMyRequestHandler.Handle(LRequest);
writeln(LResponse.MyResult);
readln;
except
on E: Exception do
writeln(E.ClassName, ': ', E.Message);
end;
finally
if Assigned(LResponse) then
LResponse.Free;
if Assigned(LRequest) then
LRequest.Free;
LContainer.Free;
end;
end.
推荐阅读
- excel - Expression.Error:我们不能将运算符 < 应用于类型 Function 和 Number
- visual-studio-code - VSCode geddski 的宏扩展:如何通过简单地写 3 个首字母来编写片段,然后在建议中选择片段?
- google-maps - 在 Gmap(Primefaces)中绘制多边形?
- react-native - 在使用 expo build:android 构建 react-native 应用程序时,我收到 MaxListenersExceededWarning 错误
- node.js - 无法使用 npm start 启动 Express.js 应用程序,它以 Docker 启动,但现在 Docker 也不起作用
- enums - Rust 枚举的问题
- list - Ocaml:即使已经找到,也要重复图中的路径
- postgresql - 使用荷兰语语言环境将字符串转换/转换为数字
- loops - DELPHI 10.3 - 循环直到按键小问题
- c++ - c ++ 11中'sizeof ...'运算符有什么用?(不是 sizeof 运算符)