Fru*_*nsi 28 c++ design-patterns
我使用的协议基本上是TCP上的请求和响应协议,类似于其他基于行的协议(SMTP,HTTP等).
该协议有大约130种不同的请求方法(例如登录,用户添加,用户更新,日志获取,文件信息,文件信息......).所有这些方法都没有很好地映射到HTTP(GET,POST,PUT,...)中使用的广泛方法.这种广泛的方法会引入一些实际含义的后续曲折.
但协议方法可以按类型分组(例如用户管理,文件管理,会话管理......).
当前服务器端实现使用class Worker
with方法ReadRequest()
(读取请求,由方法加参数列表组成),HandleRequest()
(见下文)和WriteResponse()
(写入响应代码和实际响应数据).
HandleRequest()
将为实际请求方法调用一个函数 - 使用方法名称的哈希映射到成员函数指向实际处理程序的指针.
实际的处理程序是一个普通的成员函数,每个协议方法有一个:每个都验证它的输入参数,做它必须做的事情并设置响应代码(成功是/否)和响应数据.
示例代码:
class Worker {
typedef bool (Worker::*CommandHandler)();
typedef std::map<UTF8String,CommandHandler> CommandHandlerMap;
// handlers will be initialized once
// e.g. m_CommandHandlers["login"] = &Worker::Handle_LOGIN;
static CommandHandlerMap m_CommandHandlers;
bool HandleRequest() {
CommandHandlerMap::const_iterator ihandler;
if( (ihandler=m_CommandHandlers.find(m_CurRequest.instruction)) != m_CommandHandler.end() ) {
// call actual handler
return (this->*(ihandler->second))();
}
// error case:
m_CurResponse.success = false;
m_CurResponse.info = "unknown or invalid instruction";
return true;
}
//...
bool Handle_LOGIN() {
const UTF8String username = m_CurRequest.parameters["username"];
const UTF8String password = m_CurRequest.parameters["password"];
// ....
if( success ) {
// initialize some state...
m_Session.Init(...);
m_LogHandle.Init(...);
m_AuthHandle.Init(...);
// set response data
m_CurResponse.success = true;
m_CurResponse.Write( "last_login", ... );
m_CurResponse.Write( "whatever", ... );
} else {
m_CurResponse.Write( "error", "failed, because ..." );
}
return true;
}
};
Run Code Online (Sandbox Code Playgroud)
所以.问题是:我的worker类现在有大约130个"命令处理程序方法".每个人都需要访问:
更好地构建这些命令处理程序方法的好策略是什么?
一个想法是每个命令处理程序有一个类,并使用对请求,响应对象等的引用初始化它 - 但开销是恕我直言不可接受的(实际上,它会为处理程序需要的任何单一访问添加间接:请求,响应,会话对象,...).如果它能提供实际的优势,那将是可以接受的.但是,这听起来不太合理:
class HandlerBase {
protected:
Request &request;
Response &response;
Session &session;
DBHandle &db;
FooHandle &foo;
// ...
public:
HandlerBase( Request &req, Response &rsp, Session &s, ... )
: request(req), response(rsp), session(s), ...
{}
//...
virtual bool Handle() = 0;
};
class LoginHandler : public HandlerBase {
public:
LoginHandler( Request &req, Response &rsp, Session &s, ... )
: HandlerBase(req,rsp,s,..)
{}
//...
virtual bool Handle() {
// actual code for handling "login" request ...
}
};
Run Code Online (Sandbox Code Playgroud)
好的,HandlerBase可以只为工作对象本身提供一个引用(或指针)(而不是引用请求,响应等).但这也会增加另一个间接(this-> worker-> session而不是this-> session).那个间接是没有问题的,如果它毕竟会带来一些好处.
有关整体架构的一些信息
worker对象表示与某个客户端的实际TCP连接的单个工作线程.每个线程(因此,每个工作者)都需要自己的数据库句柄,授权句柄等.这些"句柄"是每个线程对象,允许访问服务器的某个子系统.
整个体系结构基于某种依赖注入:例如,要创建会话对象,必须为会话构造函数提供"数据库句柄".然后,会话对象使用此数据库句柄来访问数据库.它永远不会调用全局代码或使用单例.因此,每个线程都可以自行运行.
但成本是 - 不仅仅是调用单例对象 - 工作者及其命令处理程序必须通过这种特定于线程的句柄访问系统的任何数据或其他代码.这些句柄定义其执行上下文.
摘要和澄清:我的实际问题
我正在寻找一种优雅的替代当前("具有大量处理程序方法的工作对象")解决方案:它应该是可维护的,具有低开销并且不需要编写太多胶水代码.此外,它必须仍允许每个方法控制其执行的非常不同的方面(这意味着:如果一个方法"super flurry foo"想要在满月开启时失败,那么该实现必须可以这样做) .这也意味着,我不希望在我的代码的这个架构层中存在任何类型的实体抽象(创建/读取/更新/删除XFoo类型)(它存在于我的代码中的不同层).这个架构层是纯协议,没有别的.
最后,它肯定会是妥协,但我对任何想法感兴趣!
AAA奖金:具有可互换协议实现的解决方案(而不仅仅是当前的class Worker
,负责解析请求和编写响应).有可能会是可互换的class ProtocolSyntax
,即处理这些协议语法的细节,但仍然使用我们的新闪亮的结构化命令处理程序.
cas*_*nca 16
你已经掌握了大部分正确的想法,以下是我将如何进行的.
让我们从你的第二个问题开始:可互换的协议.如果您有通用请求和响应对象,则可以使用读取请求和写入响应的接口:
class Protocol {
virtual Request *readRequest() = 0;
virtual void writeResponse(Response *response) = 0;
}
Run Code Online (Sandbox Code Playgroud)
你可以有一个HttpProtocol
实例,例如.
至于你的命令处理程序,"每个命令处理程序一个类"是正确的方法:
class Command {
virtual void execute(Request *request, Response *response, Session *session) = 0;
}
Run Code Online (Sandbox Code Playgroud)
请注意,我将所有常见会话句柄(DB,Foo等)汇总到一个对象中,而不是传递一大堆参数.同时使用这些方法参数而不是构造函数参数意味着您只需要每个命令的一个实例.
接下来,您将拥有一个CommandFactory
包含命令对象的命令名称映射:
class CommandFactory {
std::map<UTF8String, Command *> handlers;
Command *getCommand(const UTF8String &name) {
return handlers[name];
}
}
Run Code Online (Sandbox Code Playgroud)
如果你已经完成了这Worker
一切,那就变得极其简单,只需协调一切:
class Worker {
Protocol *protocol;
CommandFactory *commandFactory;
Session *session;
void handleRequest() {
Request *request = protocol->readRequest();
Response response;
Command *command = commandFactory->getCommand(request->getCommandName());
command->execute(request, &response, session);
protocol->writeResponse(&response);
}
}
Run Code Online (Sandbox Code Playgroud)
如果是我,我可能会在你的问题中使用两者的混合解决方案.
有一个可以处理多个相关命令的worker基类,并且可以允许主"dispatch"类探测支持的命令.对于粘合剂,您只需要告诉调度类有关每个工作类的信息.
class HandlerBase
{
public:
HandlerBase(HandlerDispatch & dispatch) : m_dispatch(dispatch) {
PopulateCommands();
}
virtual ~HandlerBase();
bool CommandSupported(UTF8String & cmdName);
virtual bool HandleCommand(UTF8String & cmdName, Request & req, Response & res);
virtual void PopulateCommands();
protected:
CommandHandlerMap m_CommandHandlers;
HandlerDispatch & m_dispatch;
};
class AuthenticationHandler : public HandlerBase
{
public:
AuthenticationHandler(HandlerDispatch & dispatch) : HandlerBase(dispatch) {}
bool HandleCommand(UTF8String & cmdName, Request & req, Response & res) {
CommandHandlerMap::const_iterator ihandler;
if( (ihandler=m_CommandHandlers.find(req.instruction)) != m_CommandHandler.end() ) {
// call actual handler
return (this->*(ihandler->second))(req,res);
}
// error case:
res.success = false;
res.info = "unknown or invalid instruction";
return true;
}
void PopulateCommands() {
m_CommandHandlers["login"]=Handle_LOGIN;
m_CommandHandlers["logout"]=Handle_LOGOUT;
}
void Handle_LOGIN(Request & req, Response & res) {
Session & session = m_dispatch.GetSessionForRequest(req);
// ...
}
};
class HandlerDispatch
{
public:
HandlerDispatch();
virtual ~HandlerDispatch() {
// delete all handlers
}
void AddHandler(HandlerBase * pHandler);
bool HandleRequest() {
vector<HandlerBase *>::iterator i;
for ( i=m_handlers.begin() ; i < m_handlers.end(); i++ ) {
if ((*i)->CommandSupported(m_CurRequest.instruction)) {
return (*i)->HandleCommand(m_CurRequest.instruction,m_CurRequest,m_CurResponse);
}
}
// error case:
m_CurResponse.success = false;
m_CurResponse.info = "unknown or invalid instruction";
return true;
}
protected:
std::vector<HandlerBase*> m_handlers;
}
Run Code Online (Sandbox Code Playgroud)
然后将它们粘合在一起你会做这样的事情:
// Init
m_handlerDispatch.AddHandler(new AuthenticationHandler(m_handlerDispatch));
Run Code Online (Sandbox Code Playgroud)