例如,如果我有这么多类在同一平台上具有相同的前缀:
在android中:
Printer *p=new AndroidPrinter();
Writer *w=new AndroidWriter();
Connector *c=new AndroidConnector();
iOS中的:
Printer *p=new IOSPrinter();
Writer *w=new IOSWriter();
Connector *c=new IOSConnector();
是否可以像这样定义类名的一部分:
#define PREFIX Android
int main(){
Printer *p=new PREFIXPrinter();
Writer *w=new PREFIXWriter();
Connector *c=new PREFIXConnector();
return 0;
}
而不是:
#define PLATFORM 0
#if PLATFORM==0
#define PRINTER AndroidPrinter
#else
#define PRINTER IOSPrinter
#endif
#if PLATFORM==0
#define WRITER AndroidWriter
#else
#define WRITER IOSWriter
#endif
#if PLATFORM==0
#define CONNECTOR AndroidConnector
#else
#define CONNECTOR IOSConnector
#endif
int main(){
Printer *p=new PRINTER();
Writer *w=new WRITER();
Connector *c=new CONNECTOR();
return 0;
}
答案 0 :(得分:18)
您可以使用静态工厂模式和一些模板专门化。你真的不需要一个完全成熟的抽象工厂,因为你不会在执行中途切换平台。这样,您可以计算出在编译时使用哪个工厂实现而没有开销(假设您的工厂方法是内联的,只是转发到operator new
)。
定义几个虚拟结构以充当平台标识符:
struct Android{};
struct IOS{};
根据这些虚拟结构编写一些工厂实现:
template <typename T>
struct ConcreteComponentFactory;
template <>
struct ConcreteComponentFactory<Android>
{
static Printer *CreatePrinter()
{ return new AndroidPrinter(); }
static Writer *CreateWriter()
{ return new AndroidWriter(); }
static Connector *CreateConnector()
{ return new AndroidConnector(); }
};
template <>
struct ConcreteComponentFactory<IOS>
{
static Printer *CreatePrinter()
{ return new IOSPrinter(); }
static Writer *CreateWriter()
{ return new IOSWriter(); }
static Connector *CreateConnector()
{ return new IOSConnector(); }
};
根据某些宏找出要使用的平台:
#define PLATFORM 0
#if PLATFORM==0
using Platform = Android;
#else
using Platform = IOS;
#endif
然后键入dede您正在处理的平台的实例:
using ComponentFactory = ConcreteComponentFactory<Platform>;
现在我们可以创建对象的实例,忘记我们所处的平台:
Writer *a = ComponentFactory::CreateWriter();
如果我们需要知道我们在哪个平台,我们可以参考Platform
typedef。
这种方法非常灵活,比基于宏的版本更安全。
答案 1 :(得分:11)
您无法拥有所需的语法。但是,您可以使用更多的预处理器魔法来实现它:
#define CONCAT_HELPER(a,b) a ## b
#define CONCAT(a,b) CONCAT_HELPER(a,b)
#define x ABC
#define MAKENAME(y) CONCAT(x,y)
int MAKENAME(def); // this defines ABCdef variable
int main() {
ABCdef = 0;
return 0;
}
但是,使用其他方法更好的方法,例如评论中建议的命名空间,或者更好的抽象工厂,如下所示:
class Factory {
public:
virtual Printer* getPrinter() = 0;
virtual Writer* getWriter() = 0;
};
class AndroidFactory: public Factory {
public:
virtual Printer* getPrinter() { return new AndroidPrinter(); }
virtual Writer* getWriter() { return new AndroidWriter(); }
};
// the same for IOS, or,
// if you really have such a similar code here,
// you can make a macros to define a factory
...
int main() {
#ifdef ANDROID
Factory* factory = new AndroidFactory();
#else
Factory* factory = new IOSFactory();
#endif
// now just pass factory pointer everywhere
doSomething(old, parameters, factory);
}
(更好的方法是使用auto_ptr
甚至unique_ptr
。)
(如果需要,您也可以让您的工厂成为单身人士。)
<强>更新强>
另一种方法是拥有两个独立的工厂类,只需typedef
即可使用其中一个:
class AndroidFactory { // no inheritance here
public:
Printer* getPrinter() {...} // no virtual here!
...
};
class IOSFactory {
...
};
#ifdef ANDROID
typedef AndroidFactory Factory;
#else
typedef IOSFactory Factory;
#endif
// note that we pass Factory* here
// so it will compile and work on both Android and IOS
void doSomething(int param, Factory* factory);
int main() {
Factory* factory = new Factory();
// and pass factory pointer around
doSomething(param, factory);
}
另请参阅TartanLlama对此方法的更高级版本的回答,包括避免传递指针的静态函数。
这种方法的优点是在编译时解决所有问题,并且不进行虚拟调用。
但是,我不认为虚拟功能会成为真正的瓶颈,因为作为工厂,它不会被多次调用。我认为你的使用模式将是
Foo* foo = factory.getFoo();
foo->doSomething();
并且大部分时间都花在doSomething()
电话上,因此getFoo()
虚拟电话费用不会成为瓶颈。
同时,这种方法的缺点是缺乏适当的继承结构,因此扩展更加困难(静态函数会使扩展更加困难)。
想象一下,你想要为Android手机和Android平板电脑设置一些不同的工厂。通过继承,您将只拥有一个基本的AndroidBaseFactory
类,它具有通用逻辑和两个用于手机和平板电脑的子类,只替换您需要的内容。如果没有继承,则必须在两个工厂类中复制所有常用功能(即使它只是return new AndroidFoo();
调用)。
此外,单元测试和模拟更简单,继承和指针传递。
事实上,“单身与一系列静态函数”的许多论点也适用于此。
答案 2 :(得分:8)
你做不到。 PREFIXPrinter();
将是一个名为PREFIXPrinter();
而不是AndroidPrinter();
你可以这样做:
#define PREFIX(a) Android##a
用法:
PREFIX(Printer)();
它会调用AndroidPrinter()
答案 3 :(得分:1)
这是不可能的,但你可以用类似的推广效果做其他事情。
正如@Biffen所指出,你可以使用namespaces
正如@Petr所指出的,你可以使用abstract factory
正如@Petr在他的回答中指出的,你可以做一些预处理魔术
您可以使用reflection