delphi中消息解码器的设计建议
我想实现一个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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
你的第二次尝试非常接近。你只是缺少一些细节。
如果您使用了虚构类型
TClassInfo
,则需要定义一个元类来表示您的请求类。我假设 TAaaRequest 和 TBbbRequest(以及其他 100 个请求类)都源自某个基类 TRequest。像这样定义TRequestClass
:FromJSON
方法对每个类执行不同的操作,对吗?如果是这样的话,那么它应该是虚拟的。 (如果该方法在每个类中执行相同的操作,那么它就不必是虚拟的,尽管其他人可能会告诉您。)您不必对方法的结果进行类型转换构造函数;只需将Info
声明为TRequest
而不是TObject
。您需要进行的最大更改是您的
IRequestHandler
接口。由于每个对象都是一个TRequest
,因此在没有巨大的if
-else
梯子的情况下分派到正确的接口方法将是笨拙的检查每个可能的类别。相反,再次使用虚拟调度。为每个
TRequest
对象提供一个虚拟Handle
方法,因此类声明将如下所示:为每个后代实现
Handle
,然后就完成了。最终,IRequestHandler
接口可能会消失。您已经将处理能力写入每个请求类中。您不需要一个类来表示请求,而另一个类来处理它。如果您想要一个单独的处理类,那么您可以使用已有的处理类,您将有一个很大的条件来决定要调用哪个
IRequestHandler
方法,或者您有很多请求处理程序对象都实现相同的接口,您可以像决定创建哪个请求类一样决定创建哪个请求处理程序对象。然后将请求对象交给请求处理程序对象并让它们一起工作。例如,定义您的处理程序接口:
像注册请求一样注册处理程序:
像处理请求一样实例化处理程序:
然后将请求传递给处理程序:
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 assumeTAaaRequest
andTBbbRequest
(and the 100 other request classes) all descend from some baseTRequest
class. DefineTRequestClass
like this: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 declareInfo
as aTRequest
instead of aTObject
.The biggest change you'll need to make is to your
IRequestHandler
interface. Since every one of your objects is aTRequest
, it will be clumsy to dispatch to the right interface method without having a giantif
-else
ladder to check each possible class.Instead, use virtual dispatch again. Give each
TRequest
object a virtualHandle
method, so the class declaration will look like this:Implement
Handle
for each descendant, and you're done. Ultimately, theIRequestHandler
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:
Register handlers like you register requests:
Instantiate handlers like you do requests:
Then pass the request to the handler: