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

发布于 2024-08-27 18:46:51 字数 2625 浏览 4 评论 0原文

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

如有任何提示,我们将不胜感激。

I want to implement a RPC module. Different requests are encoded as JSON objects. They will be decoded and then be handled by a request handler. At last a corresponding response will be returned. The demo code looks as follows:

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;

According to the code, the handling of different request types are very similar. If I have 100 different request types, I have to copy and paste the above code block 100 times. This is not clever. I am looking for a better way to do the same logic. My imagination is as follows:

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;

I do not know, if I can write an universal way to handle a great number of different request types. Development environment Delphi 2010.

Any hint is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

拥醉 2024-09-03 18:46:51

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

如果您使用了虚构类型 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);

Your second attempt is very close. You're only missing a couple of details.

Where you've used the made-up type TClassInfo, you need to define a metaclass to represent your request classes. I assume TAaaRequest and TBbbRequest (and the 100 other request classes) all descend from some base TRequest class. Define TRequestClass like this:

type
  TRequestClass = class of TRequest;

The FromJSON method does something different for each class, right? If that's the case, then it should be virtual. (If the method does the same thing in each class, then it doesn't have to be virtual, despite what others may tell you.) You don't have to type-cast the result of the constructor; simply declare Info as a TRequest instead of a TObject.

The biggest change you'll need to make is to your IRequestHandler interface. Since every one of your objects is a TRequest, it will be clumsy to dispatch to the right interface method without having a giant if-else ladder to check each possible class.

Instead, use virtual dispatch again. Give each TRequest object a virtual Handle method, so the class declaration will look like this:

type
  TRequest = class
  public
    constructor FromJSON(const json: string);
    function Handle: TResponse; virtual; abstract;
  end;

Implement Handle for each descendant, and you're done. Ultimately, the IRequestHandler interface can go away. You've already written the handling ability into each of the request classes. You don't need one class to represent the request and another class to handle it.

If you want to have a separate handling class, then you could either go with what you have already, where you'll have a big conditional to decide which IRequestHandler method you'll call, or you have lots of request-handler objects all implement the same interface, and you decide which one to create the same way you decide which request class to create. Then you give the request object to the request-handler object and let them work together.

For example, define your handler interface:

type
  IRequestHandler = interface
    function Handle(request: TRequest): TResponse;
  end;

Register handlers like you register requests:

// Use the same names as the requests, but a different dictionary
FRequestHandlers.Add(TAaaRequest.ClassName, TAaaHandler);
FRequestHandlers.Add(TBbbRequest.ClassName, TBbbHandler);

Instantiate handlers like you do requests:

HandlerType := FRequestHandlers[Method];
HandlerObject := HandlerType.Create;
if not Supports(HandlerObject, IRequestHandler, Handler) then
  exit;

Then pass the request to the handler:

Result := Handler.Handle(Request);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文