使用场景
工厂模式应用很广,重构场景:
1. 冗长switch
如果case比较多而且每个case处理处理程序比较复杂(即使将其封装到函数也不容易管理,而且可读性不高,每次要重新阅读case),都可以通过"简单工厂模式"优化:将
//优化前
{
switch(cmd) {
case HTTP_REQ_CMD:
process_http_req(); break;
case RPC_REQ_CMD:
process_rpc_req(); break;
case:...
}
//优化后
class RequestProcess {
public: virtual bool process() = 0;
};
class HttpRequestProcess : public RequestProcess {
public: virtual bool process() {}
};
class RpcRequestProcess : public RequestProcess {
public: virtual bool process() {}
};
class RequestProcessFactory {
public: static RequestProcess* createProcessor(int cmd) {
RequestProcess* processor = nullptr;
switch(cmd) {
case HTTP_REQ_CMD:
processor = new HttpRequestProcess(); break;
case RPC_REQ_CMD:
processor = new RpcRequestProcess(); break;
}
return processor;
}
};
{
//业务处理
RequestProcess* processor = RequestProcessFactory::createProcessor(cmd);
if (processor) {
processor->process();
} else {
LOG("process is null");
}
}
}
简单工厂模式
一个工厂类生产一类产品(属于一个继承类簇),在工厂方法createObject里传入产品参数就new出指定的产品,产品参数一般是类名和构造对象时需要的构造函数实参。
class Factory{
public:
static BaseRequest* createObj(string name) {
if (name == "HttpRequest") {
return new HttpRequest;
} else if (...) {
...
}
}
};
缺点:当增加新的业务场景,需要添加新的请求类,定义完子类后还要修改工厂类,违背开闭原则(只修改提供方,不修改适用方)。
改进1:使用反射
为了不修改工厂类,我们可以把实例化对象的部分拿出来(毕竟同一类簇的类也可能有不同的实例化方法),并利用map做类名到实例化方法的映射,每定义一个类,就注册其实例方法。
namespace src {
//定义Class来存储类名所对应类的实例化方式,并提供注册接口
typedef void* (*instance_func)();
class Class {
public:
static void* new_instance(std::string class_name) {
auto iter = _class_map.find(class_name);
if (iter == _class_map.end()) {
return NULL;
}
return iter->second();
}
static void register_class(std::string class_name, instance_func func) {
_class_map[class_name] = func;
}
private:
static std::map<std::string, instance_func> _class_map; //需要在cpp文件中初始化
};
}
}
//通过宏定义,每定义一个类就注册其实例方法
#define REGISTER_CLASS(class_name) \
namespace { \
void* class_name##instance_func() { \
return new (std::nothrow) class_name; \
} \
__attribute__((constructor)) void class_name##register_class() { \
src::Class::register_class(#class_name, class_name##instance_func); \
} \
}
那么工厂类就固定了,为了方便也可以使用宏定义:该类簇的工厂方法。
#define DEFINE_FACTORY(base_class) \
class base_class##Factory { \
public: \
static base_class* get_instance(std::string class_name) { \
void* ptr = src::Class::new_instance(class_name); \
return (base_class*)ptr; \
} \
};
当定义基类BaseRequest是,要注册类的实例化方法,同时定义工厂类,子类定义时只需注册类的实例化方法。
//base_request.h
class BaseRequest {};
REGISTER_CLASS(BaseRequest)
DEFINE_FACTORY(BaseRequest)
//http_request.h
class HttpRequest {};
REGISTER_CLASS(HttpRequest)
//rpc_request.h
class RpcRequest {};
REGISTER_CLASS(RpcRequest)
NOTE:
简单工厂模式是用来生产同一类簇的对象
为解决不同子类有不同实例化方法问题,将类名和相应的实例化方法存到map中。
由于存储行为应该在main函数之前执行,因此要靠宏定义的__attribute__(constructor)来实现
改进2: 工厂方法
工厂方法
一个工厂只生产一个类的对象的方式,产品在一个类簇,相应工厂类也是一个类簇。
class HttpRequestFactory : public RequestFactory {
public:
static HttpRequestFactory* createObj() {
return new HttpRequestFactory;//只生产一个类的对象
}
}
缺点:当新增业务场景时,不仅要定义子类,还要定义相应的工厂类,类数目成倍增加,增加编译难度。