2010-04-06 15 views
1

我想实现一个RPC模块。不同的请求被编码为JSON对象。它们将被解码,然后由请求处理程序处理。最后会返回相应的响应。演示代码如下所示:delphi的消息解码器的设计建议

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

你应该按照你的想象力,看看它会带领:-) – 2010-04-06 03:40:04

回答

1

你的第二次尝试非常接近。你只是错过了一些细节。

如果您已经使用化妆类型TClassInfo,您需要定义元类来表示您的请求类。我假设TAaaRequestTBbbRequest(和其他100个请求类)全部从一些基类TRequest类下降。定义TRequestClass这样的:

type 
    TRequestClass = class of TRequest; 

FromJSON方法做了每一类不同的,对不对?如果是这样的话,那应该是虚拟的。 (如果这个方法在每个类中都做了同样的事情,那么就不会是虚拟的,尽管别人可能会告诉你。)你不必输入构造函数的结果;简单地将Info声明为TRequest而不是TObject

您需要做出的最大改变是您的IRequestHandler界面。由于您的每个对象都是TRequest,因此如果没有巨大的梯子来检查每个可能的类,就会派发到正确的界面方法,这会很笨拙。

取而代之,再次使用虚拟调度。给每个TRequest物体的虚拟Handle方法,所以类的声明看起来是这样的:

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

实现Handle每个后代,就大功告成了。最终,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); 
+0

感谢您的解决方案。但是TRequest似乎应该实现Handle方法。我希望这部分与TRequest对象隔离。 – stanleyxu2005 2010-04-10 21:42:15

+0

好吧,好的。然后去我的第二个建议,并有一个单独的处理程序类。 – 2010-04-11 02:47:09