我想以一种允许我编译代码而不引入类型依赖的方式实现工厂(或其他模式)。
enum CarType
{
BMW,
PORSCHE,
MERC
};
class CarFactory
{
public:
static Car* create(CarType type)
{
switch(type)
{
case BMW : return new BMWCar();
case PORSCHE : return new PorscheCar();
default : return new MercCar();
}
}
};
当我编译CarFactory时,我需要将BMWCar,PorscheCar和MercCar作为我的编译/链接单元的一部分。
我的代码库设置方式,我们可能只想运送BMWCar,或者两个或全部三个。所以,我无法使create()
依赖于类型。
我如何调整工厂模式?另外,我想避免使用ifdef,因为这只是我问题的一个例子。真正的代码库是巨大的,并不是ifdef代码的实用解决方案。
更新: 另外,我不允许使用:
这主要是由于客户构建工具链限制。我无法改变这些。
答案 0 :(得分:2)
我通常会做类似的事情:
class CarFactory
{
public:
static void RegisterCar(CarType t, std::function<Car*()> f)
{
getMap().emplace(t, f);
}
static Car* create(CarType type)
{
return getMap().at(type)();
}
private:
static std::unorderd_map<CarType, std::function<Car*()> >& getMap()
{
static std::unorderd_map<CarType, std::function<Car*()> > m;
return m;
}
};
在每个班级实施中:
class BMWCar : public Car
{
struct Init
{
Init()
{
CarFactory::RegisterCar(BMW, [](){return new BMWCar(); });
}
};
static Init initializeBmwCar;
/** .. */
};
/*** BMWCar.cpp ***/
BMWCar::Init BMWCar::initializeBmwCar;
这是有效的,因为每个类型在静态初始化期间使用static Init
对象初始化自己的工厂。
这段代码的巨大痛苦是为了避免初始化顺序惨败:一个简单的实现只会在CarFactory
中使用静态映射。遗憾的是,无法保证BMWCar::initializeBmwCar
构造函数在<{1}}中地图之后运行。有时一些编译器可能会工作,有时它可能会崩溃。
因此,我们的想法是使用静态函数(CarFactory
)和静态变量(getMap
),保证在第一次调用m
时进行初始化。
我知道getMap
/ clang
使用此模式注册优化传递。
另一个更复杂但更灵活的解决方案是设计一个插件系统,其中每个DLL实现一个llvm
类型并导出一个car
函数。
然后,您可以在初始化期间通过动态加载库并调用CreateCar
/ CreateCar
来收集所有这些GetProcAddress
。
在Windows上实现这两个解决方案可能都很棘手,因为(除非dlsym
是抽象的)基础Car
实现需要进入自己的库,每个插件dll需要链接到该库
答案 1 :(得分:1)
这是一个完整的例子,C ++ 98风格。我假设在编译时不知道可能的汽车类型列表,所以我将枚举更改为字符串。
cartype.hh:
#include <map>
#include <string>
#include <vector>
struct Car {
virtual std::string type() = 0;
virtual ~Car() {}
};
// Factory
class CarFactory
{
typedef std::map<std::string, Car *(*)()> Registry;
static Registry ®istry();
public:
static std::vector<std::string> getRegisteredTypes();
static void registerCarType(std::string type, Car *(*creator)());
static Car* create(std::string type);
};
cartype.cc:
#include <map>
#include <string>
#include <vector>
#include "cartype.hh"
// Factory
CarFactory::Registry &CarFactory::registry()
{
static std::map<std::string, Car *(*)()> r;
return r;
}
std::vector<std::string> CarFactory::getRegisteredTypes()
{
static const Registry ® = registry();
std::vector<std::string> types;
types.reserve(reg.size());
Registry::const_iterator end = reg.end();
for(Registry::const_iterator it = reg.begin(); it != end; ++it)
types.push_back(it->first);
return types;
}
void CarFactory::registerCarType(std::string type, Car *(*creator)())
{
registry()[type] = creator;
}
Car* CarFactory::create(std::string type)
{
static const Registry ® = registry();
Registry::const_iterator result = reg.find(type);
if(result != reg.end())
return result->second();
throw "Unregistered car type";
}
bmw.cc(porsche.cc和merc.cc相似但未显示):
#include <string>
#include "cartype.hh"
// BMW
class BMWCar : public Car
{
static const bool registered;
static Car *create() { return new BMWCar; }
public:
virtual std::string type() { return "BMW"; }
};
const bool BMWCar::registered =
(CarFactory::registerCarType("BMW", BMWCar::create),
true);
check.cc:
#include <iostream>
#include <memory>
#include <ostream>
#include <string>
#include <vector>
#include "cartype.hh"
int main()
{
// all car types should be registered when we enter main
std::vector<std::string> types = CarFactory::getRegisteredTypes();
for(std::size_t i = 0; i < types.size(); ++i)
{
std::auto_ptr<Car> car(CarFactory::create(types[i]));
std::cout << "Wanted: " << types[i] << ", Got: " << car->type() << std::endl;
}
}
编译并运行:
-*- mode: compilation; default-directory: "/tmp/" -*-
Compilation started at Tue Aug 4 01:24:51
set -ex; g++ -std=c++98 -g -O3 -Wall check.cc cartype.cc bmw.cc porsche.cc -o check; ./check
+ g++ -std=c++98 -g -O3 -Wall check.cc cartype.cc bmw.cc porsche.cc -o check
+ ./check
Wanted: BMW, Got: BMW
Wanted: PORSCHE, Got: Porsche
Compilation finished at Tue Aug 4 01:24:54
注1:您不能假设所有类都在main启动之前注册,即在您可能正在进行的其他静态初始化中。
注2:如果Car
实现位于自己的共享对象库(.so)中,那么(实际上大多数解决方案)可能无法自行运行。除非二进制文件需要来自该.so的符号,否则链接器根本不会将该依赖项放在完整的二进制文件中。因此,您需要特殊的链接器选项来强制链接器执行此操作。对于使--as-needed
成为默认值的发行版来说,这主要是一个问题(我正在看着你,Ubuntu)。使用--no-as-needed
或-Wl,--no-as-needed
将其关闭,至少对于包含汽车实施的库而言。
静态库(.a)存在类似问题。 .a文件只是几个.o文件的集合,链接器将只包含.a文件中包含以前未定义的符号的那些.o文件。可以强制链接器考虑使用-u symbol_name
未定义的符号。但那是受损的符号名称,因此很难猜测。在我的示例中,为此目的起作用的一个符号是_ZN6BMWCar10registeredE
,a.k.a BMW::registered
以未拼写的形式出现。但是使用C链接定义函数可能更好,因此您不需要猜测错位变量名称:
extern "C" void bmw_mark() { }
然后您不必猜测符号名称,只需使用-u bmw_mark
即可。
这必须在与BMWCar
的其他定义相同的编译单元中完成,因此它们最终位于相同的.o文件中。
答案 2 :(得分:0)
您可以使用模板和专业化,而不是使用switch
。
以下示例提供了BMWCar
和MercCar
的实现,但不包括PorscheCar
:
enum CarType
{
BMW,
PORSCHE,
MERC
};
struct Car {};
struct BMWCar:public Car{};
// DO NOT SHIP
// struct PorscheCar:public Car{};
struct MercCar:public Car{};
template <CarType type>
struct CarFactory;
template <>
struct CarFactory<BMW>
{
static Car* create()
{
return new BMWCar();
}
};
/*
// DO NOT SHIP
template <>
struct CarFactory<PORSCHE>
{
static Car* create()
{
return new PorscheCar();
}
};
*/
template <>
struct CarFactory<MERC>
{
static Car* create()
{
return new MercCar();
}
};
int main()
{
Car* m = CarFactory<MERC>::create();
}
答案 3 :(得分:0)
您可以将类型动态注册到数组。我想到的第一个解决方案就是(你可能想要一个更好的设计):
{{1}}
答案 4 :(得分:0)
我的解决方案:
class CarCreator
{
public:
virtual Car* operator(int otherArgs) = 0;
};
class BMWCarCreator : pure CarCreator
{
public:
Car* operator(int otherArgs) { return new BMWCar(otherArgs); }
};
// in BMWCar.cpp
class BMWCar : public Car
{
// SOME WAY TO STATICALLY REGISTER BMWCarCreator to BMWCarType
BMWCar( int otherArgs ) { }
};
class CarFactory
{
public:
// associates the type-creator_FnObj
void registerCreator(CarType type, CarCreator* creator);
// Creates the car based on associated CarCreator*
Car* create(CarType type, int otherArgs)
{
CarCreator* creator = this->findCreatorAssociation(type);
if (!creator)
throw exception;
return creator(otherArgs);
}
}
我仍然需要弄明白:
答案 5 :(得分:0)
我在这件事上并不是一个好的程序员。但是,试着给出我最好的答案。
#include <iostream>
using namespace std;
class Car { virtual void SomeMethods() {} /* Your Code */ };
class BMWCar : public Car { /* Your Code */ };
class PorscheCar : public Car { /* Your Code */ };
class MercCar : public Car { /* Your Code */ };
enum CarType
{
BMW,
PORSCHE,
MERC,
BMW_N_PORSCHE,
BMW_N_MERC,
PORSCHE_N_MERC,
ALL
};
class FinalCar
{
private:
Car* m_car[3];
size_t m_size;
CarType m_CarType;
public:
FinalCar(Car* car1, CarType carType)
{
m_car[0] = car1;
m_car[1] = nullptr;
m_car[2] = nullptr;
m_size = 1;
m_CarType = carType;
}
FinalCar(Car* car1, Car* car2, CarType carType)
{
m_car[0] = car1;
m_car[1] = car2;
m_car[2] = nullptr;
m_size = 2;
m_CarType = carType;
}
FinalCar(Car* car1, Car* car2, Car* car3, CarType carType)
{
m_car[0] = car1;
m_car[1] = car2;
m_car[2] = car3;
m_size = 3;
m_CarType = carType;
}
size_t GetSize()
{
return m_size;
}
CarType GetCarType()
{
return m_CarType;
}
Car* GetCar(size_t n)
{
return m_car[n];
}
~FinalCar()
{
if (m_car[0] != nullptr)
{
delete m_car[0];
m_car[0] = nullptr;
}
if (m_car[1] != nullptr)
{
delete m_car[1];
m_car[1] = nullptr;
}
if (m_car[2] != nullptr)
{
delete m_car[2];
m_car[2] = nullptr;
}
}
};
class CarFactory
{
public:
static FinalCar create(CarType type)
{
switch (type)
{
case BMW:
return FinalCar(new BMWCar(), BMW);
break;
case PORSCHE:
return FinalCar(new PorscheCar(), PORSCHE);
break;
case MERC:
return FinalCar(new MercCar(), MERC);
break;
case BMW_N_PORSCHE:
return FinalCar(new BMWCar(), new PorscheCar(), BMW_N_PORSCHE);
break;
case BMW_N_MERC:
return FinalCar(new BMWCar(), new MercCar(), BMW_N_MERC);
break;
case PORSCHE_N_MERC:
return FinalCar(new PorscheCar(), new MercCar(), PORSCHE_N_MERC);
break;
default:
return FinalCar(new BMWCar(), new PorscheCar(), new MercCar(), ALL);
break;
}
}
};
int main()
{
FinalCar myCar = CarFactory::create(PORSCHE_N_MERC);
for (int i = 0; i < myCar.GetSize(); i++)
{
Car* tmpCar = myCar.GetCar(i);
if (dynamic_cast<BMWCar*>(tmpCar))
{
cout << "BMWCar*" << endl;
}
else if (dynamic_cast<PorscheCar*>(tmpCar))
{
cout << "PorscheCar*" << endl;
}
else if (dynamic_cast<MercCar*>(tmpCar))
{
cout << "MercCar*" << endl;
}
}
}