在delphi中设计消息解码器的建议

时间:2010-04-06 03:23:02

标签: delphi class templates methods

我想实现一个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。

任何提示都表示赞赏。

1 个答案:

答案 0 :(得分:1)

你的第二次尝试非常接近。你只缺少一些细节。

如果您使用了虚拟类型TClassInfo,则需要定义元类来表示您的请求类。我假设TAaaRequestTBbbRequest(以及其他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);