我有一个抽象基类
class IThingy
{
virtual void method1() = 0;
virtual void method2() = 0;
};
我想说 - “所有提供具体实例化的类都必须提供这些静态方法”
我很想做
class IThingy
{
virtual void method1() = 0;
virtual void method2() = 0;
static virtual IThingy Factory() = 0;
};
我知道没有编译,无论如何它还不清楚如何使用它,即使它已编译。无论如何我只能做
Concrete::Factory(); // concrete is implementation of ITHingy
根本没有在基类中提及Factory。
但我觉得应该有某种方式来表达我希望实现注册的合同。
这有一个众所周知的习语吗?或者我只是把它放在评论中?也许我不应该试图强迫这个
编辑:当我输入问题时,我觉得自己模糊不清。我觉得应该有一些方法来表达它。伊戈尔给出了一个优雅的答案,但实际上它表明它确实无济于事。我最终还是要做
IThingy *p;
if(..)
p = new Cl1();
else if(..)
p = new Cl2();
else if(..)
p = new Cl3();
etc.
我想像c#,python或java这样的反射语言可以提供更好的解决方案
答案 0 :(得分:33)
您遇到的问题部分与轻微违反单一责任原则有关。您试图通过界面强制执行对象创建。接口应该更纯粹,并且只包含与接口应该做的不可分割的方法。
相反,您可以从界面中获取创建(所需的virtual static
方法)并将其放入工厂类中。
这是一个简单的工厂实现,它强制在派生类上使用工厂方法。
template <class TClass, class TInterface>
class Factory {
public:
static TInterface* Create(){return TClass::CreateInternal();}
};
struct IThingy {
virtual void Method1() = 0;
};
class Thingy :
public Factory<Thingy, IThingy>,
public IThingy {
//Note the private constructor, forces creation through a factory method
Thingy(){}
public:
virtual void Method1(){}
//Actual factory method that performs work.
static Thingy* CreateInternal() {return new Thingy();}
};
用法:
//Thingy thingy; //error C2248: 'Thingy::Thingy' : cannot access private member declared in class 'Thingy'
IThingy* ithingy = Thingy::Create(); //OK
通过从Factory<TClass, TInterface>
中删除,派生类被编译器强制拥有CreateInternal方法。没有定义它会导致这样的错误:
错误C2039:'CreateInternal':不是 'Thingy'的成员
答案 1 :(得分:0)
没有确定的方法在C ++中规定这样的合同,因为也没有办法使用这种多态,因为行
Concrete::Factory()
总是一个静态编译时间的东西,也就是说,你不能写这行Concrete
是一个未知的客户端提供的类。
您可以让客户实现这种“合同”,使其比不提供更方便。例如,您可以使用CRTP:
class IThingy {...};
template <class Derived>
class AThingy : public IThingy
{
public:
AThingy() { &Derived::Factory; } // this will fail if there is no Derived::Factory
};
并告诉客户端派生自AThingy<their_class_name>
(您可以通过构造函数可见性调整来强制执行此操作,但您无法确保客户端不会对their_class_name
撒谎。)
或者您可以使用经典解决方案,创建工厂类的单独层次结构,并要求客户向您的API提供他们的ConcreteFactory
对象。
答案 2 :(得分:0)
在C ++中,静态方法不能虚拟(或抽象)。
要执行您想要的操作,您可以拥有一个返回具体实例的IThingy::factory
方法,但您需要以某种方式为工厂提供创建实例的方法。例如,定义一个方法签名,如IThing* (thingy_constructor*)()
,并在IThingy
中进行静态调用,您可以将此类函数传递给定义IThingy
构建工厂实例的方式。然后,在依赖库或类中,您可以使用适当的函数调用此方法,该函数反过来说明如何正确构造实现您的接口的对象。
如果您没有调用工厂的“初始化程序”,则需要采取适当的措施,例如抛出异常。