我想实现一个RPC模块。不同的请求被编码为JSON对象。它们将被解码,然后由请求处理程序处理。最后将返回相应的响应。演示代码如下所示:
type
IRequestHandler = interface
function Handle(const Request: TAaaRequest): TResponse;
function Handle(const Request: TBbbRequest): TResponse;
end;
TDecoder = class
class function Decode(const Json: TJsonObject; const RequestHandler: IRequestHandler): TResponse;
end;
class function TDecoder.Decode(const Json: TJsonObject; const RequestHandler: IRequestHandler): TResponse;
var
Method: string;
Request: TObject;
begin
Method := Json['method'].AsString;
if (Method = TAaaRequest.ClassName) then
begin
Request := TAaaRequest.FromJSON(Json); // Casted as TObject
if Request <> nil then
begin
Result := RequestHandler.Handle(TAaaRequest(Request));
Request.Free;
end;
end
else if (Method = TBbbRequest.ClassName) then
begin
Request := TBbbRequest.FromJSON(Json); // Casted as TObject
if Request <> nil then
begin
Result := RequestHandler.Handle(TBbbRequest(Request));
Request.Free;
end;
end
else
Result := CreateErrorResponse('Unknown method: ' + Json.ToString);
end;
根据代码,不同请求类型的处理非常相似。如果我有100种不同的请求类型,我必须复制并粘贴上述代码块100次。这不聪明。我正在寻找一种更好的方法来做同样的逻辑。我的想象如下:
TDecoder = class
private
FRequestTypes: TDictionary<string, TClassInfo>; // Does this work?
public
constructor Create;
destructor Destroy; override;
function Decode(const Json: TJsonObject; const RequestHandler: IRequestHandler): TResponse;
end;
constructor TDecoder.Create;
begin
FRequestTypes := TDictionary<string, TClassInfo>.Create;
FRequestTypes.Add(TAaaRequest.ClassName, TAaaRequest); // Does this work?
FRequestTypes.Add(TBbbRequest.ClassName, TBbbRequest);
end;
destructor TDecoder.Destroy;
begin
FRequestTypes.Free;
inherited;
end;
function TDecoder.Decode(const Json: TJsonObject; const RequestHandler: IRequestHandler): TResponse;
var
Method: string;
Info: TClassInfo;
Request: TObject;
begin
Method := Json['method'].AsString;
if FRequestTypes.ContainsKey(Method) then
begin
// An universal way
Info := FRequestTypes[Method];
Request := Info.FromJSON(Json); // Casted as TObject
if Request <> nil then
begin
Result := RequestHandler.Handle(Info(Request)); // Casted to corresponding class type (e.g. TAaaRequest or TBbbRequest)
Request.Free;
end;
end
else
Result := CreateErrorResponse('Unknown method: ' + Json.ToString);
end;
我不知道,如果我能编写一种通用的方法来处理大量不同的请求类型。开发环境Delphi 2010。
任何提示都表示赞赏。
答案 0 :(得分:1)
你的第二次尝试非常接近。你只缺少一些细节。
如果您使用了虚拟类型TClassInfo
,则需要定义元类来表示您的请求类。我假设TAaaRequest
和TBbbRequest
(以及其他100个请求类)都来自某个基类TRequest
类。像这样定义TRequestClass
:
type
TRequestClass = class of TRequest;
FromJSON
方法为每个类做了不同的事情,对吧?如果是这种情况,那么它应该是虚拟的。 (如果方法在每个类中都做同样的事情,那么它不会 是虚拟的,尽管其他人可能告诉你。)你不必输入结果构造函数;只需将Info
声明为TRequest
而不是TObject
。
您需要做出的最大改变是IRequestHandler
界面。由于您的每个对象都是TRequest
,因此在没有巨型if
- else
梯形图来检查每个可能的类的情况下调度到正确的接口方法将是笨拙的。
相反,请再次使用虚拟调度。为每个TRequest
对象提供一个虚拟Handle
方法,因此类声明将如下所示:
type
TRequest = class
public
constructor FromJSON(const json: string);
function Handle: TResponse; virtual; abstract;
end;
为每个后代实施Handle
,您就完成了。最终,IRequestHandler
界面可能会消失。您已经将处理能力编写到每个请求类中。您不需要一个类来表示请求,也不需要另一个类来处理它。
如果你想要一个单独的处理课程,那么你可以选择你已经拥有的,你将有一个很大的条件来决定你将调用哪种IRequestHandler
方法,或者你有很多请求处理程序对象的所有对象都实现相同的接口,您可以决定创建哪个接口来决定创建哪个请求类。然后,将请求对象提供给请求处理程序对象,让它们一起工作。
例如,定义处理程序接口:
type
IRequestHandler = interface
function Handle(request: TRequest): TResponse;
end;
注册处理程序,例如注册请求:
// Use the same names as the requests, but a different dictionary
FRequestHandlers.Add(TAaaRequest.ClassName, TAaaHandler);
FRequestHandlers.Add(TBbbRequest.ClassName, TBbbHandler);
像您一样实例化处理程序:
HandlerType := FRequestHandlers[Method];
HandlerObject := HandlerType.Create;
if not Supports(HandlerObject, IRequestHandler, Handler) then
exit;
然后将请求传递给处理程序:
Result := Handler.Handle(Request);