2

编辑:小修复(虚拟打印;返回 mpInstance)在答案中的注释之后。

我正在尝试创建一个系统,在该系统中我可以从任何基类派生一个子类,并且它的实现应该替换基类的实现。

所有创建和使用基类对象的对象都不应改变它们创建或调用对象的方式,即即使它们实际创建了子类,也应继续调用 BaseClass.Create()。基类知道它们可以被覆盖,但它们不知道覆盖它们的具体类。

我希望所有子类的注册都在一个地方完成。

这是我的实现:

class CAbstractFactory
{
public:
    virtual ~CAbstractFactory()=0;
};

template<typename Class>
class CRegisteredClassFactory: public CAbstractFactory
{
public:
    ~CRegisteredClassFactory(){};
    Class* CreateAndGet()
    {
        pClass = new Class;
        return pClass;
    }
private:
    Class* pClass;
};

// holds info about all the classes that were registered to be overridden
class CRegisteredClasses
{
public:
    bool find(const string & sClassName);
    CAbstractFactory* GetFactory(const string & sClassName)
    {
        return mRegisteredClasses[sClassName];
    }
    void RegisterClass(const string & sClassName, CAbstractFactory* pConcreteFactory);
private:
    map<string, CAbstractFactory* > mRegisteredClasses;

};


// Here I hold the data about all the registered classes. I hold statically one object of this class.
// in this example I register a class CChildClass, which will override the implementation of CBaseClass, 
// and a class CFooChildClass which will override CFooBaseClass
class RegistrationData
{
    public:
        void RegisterAll()
        {
            mRegisteredClasses.RegisterClass("CBaseClass", & mChildClassFactory);
            mRegisteredClasses.RegisterClass("CFooBaseClass", & mFooChildClassFactory);
        };
        CRegisteredClasses* GetRegisteredClasses(){return &mRegisteredClasses;};
    private:    
        CRegisteredClasses mRegisteredClasses;
        CRegisteredClassFactory<CChildClass> mChildClassFactory;
        CRegisteredClassFactory<CFooChildClass> mFooChildClassFactory;
};

static RegistrationData StaticRegistrationData;

// and here are the base class and the child class 
// in the implementation of CBaseClass::Create I check, whether it should be overridden by another class.
class CBaseClass
{
public:
    static CBaseClass* Create()
    {
        CRegisteredClasses* pRegisteredClasses = StaticRegistrationData.GetRegisteredClasses();
        if (pRegisteredClasses->find("CBaseClass"))
        {
            CRegisteredClassFactory<CBaseClass>* pFac = 
                dynamic_cast<CRegisteredClassFactory<CBaseClass>* >(pRegisteredClasses->GetFactory("CBaseClass"));

            mpInstance = pFac->CreateAndGet();
        }
        else
        {
            mpInstance = new CBaseClass;
        }
        return mpInstance;
    }
    virtual void Print(){cout << "Base" << endl;};
private:
    static CBaseClass* mpInstance;

};

class CChildClass : public CBaseClass
{
public:
    void Print(){cout << "Child" << endl;};
private:

};

使用此实现,当我从其他类执行此操作时:

StaticRegistrationData.RegisterAll();
CBaseClass* b = CBaseClass::Create();
b.Print();

我希望在输出中得到“孩子”。

你觉得这个设计怎么样?我是不是把事情复杂化了太多,可以做得更容易?我可以创建一个从抽象类继承的模板吗?

我不得不使用 dynamic_pointer (否则没有编译) - 是否暗示有问题?

谢谢你。

4

5 回答 5

2

这种模式相当普遍。我不是 C++ 专家,但在 Java 中你随处可见。动态转换似乎是必要的,因为编译器无法判断您在地图中存储了哪种工厂。据我所知,目前的设计对此无能为力。了解这些对象的用途将有所帮助。让我举一个例子,说明如何在 Java 的数据库库 (JDBC) 中完成类似的任务:

系统有一个知道 JDBC 驱动程序的 DriverManager。必须以某种方式注册驱动程序(细节不重要);每当您请求数据库连接时,一旦注册,您就会得到一个 Connection 对象。通常这个对象将是一个 OracleConnection 或一个 MSSQLConnection 或类似的东西,但客户端代码只看到“连接”。要获得一个 Statement 对象,你可以说 connection.prepareStatement,它返回一个 PreparedStatement 类型的对象;除了它实际上是一个 OraclePreparedStatement 或 MSSQLPreparedStatement。这对客户端是透明的,因为 Statements 的工厂位于 Connection 中,而 Connections 的工厂位于 DriverManager 中。

如果您的类具有相似的相关性,您可能希望有一个返回特定类型类的函数,就像 DriverManager 的 getConnection 方法返回一个 Connection。无需铸造。

您可能要考虑的另一种方法是使用一个工厂,该工厂对您需要的每个特定类都有一个工厂方法。那么您只需要一个 factory-factory 即可获得 Factory 的实例。示例(对不起,如果这不是正确的 C++):

class CClassFactory
{
  public:
    virtual CBaseClass* CreateBase() { return new CBaseClass(); }
    virtual CFooBaseClass* CreateFoo() { return new CFooBaseClass();}
}

class CAImplClassFactory : public CClassFactory
{
  public:
    virtual CBaseClass* CreateBase() { return new CAImplBaseClass(); }
    virtual CFooBaseClass* CreateFoo() { return new CAImplFooBaseClass();}
}

class CBImplClassFactory : public CClassFactory // only overrides one method
{
  public:
    virtual CBaseClass* CreateBase() { return new CBImplBaseClass(); }
}

至于批评使用继承的其他评论:在我看来,接口和公共继承之间没有区别;所以继续在有意义的地方使用类而不是接口。从长远来看,纯接口可能更灵活,但可能不会。如果没有有关您的类层次结构的更多详细信息,就不可能说。

于 2008-12-23T14:29:17.897 回答
1

通常,当您在基类中有一个接口并且该接口在派生类中实现(IS-A 关系)时,使用基类/派生类模式。在您的情况下,基类似乎与派生类没有任何联系 - 它也可能是 void*。

如果基类和派生类之间没有联系,为什么要使用继承?如果工厂的产出不能以通用方式使用,那么拥有工厂有什么好处?你有

class CAbstractFactory
{
public:
    virtual ~CAbstractFactory()=0;
};

这是完全错误的。工厂必须制造可以立即使用的东西:

class CAbstractFactory
{
public:
    virtual ~CAbstractFactory(){};
public:
    CBaseClass* CreateAndGet()
    {
        pClass = new Class;
        return pClass;
    }
private:
    CBaseClass* pClass;

protected:
    CBaseClass *create() = 0;

};

通常,您以不应该混合的方式混合继承、虚函数和模板。

于 2008-12-23T14:49:46.390 回答
0

在没有阅读所有代码或进入细节的情况下,您似乎应该执行以下操作:

  • 类型为bCChildClass,
  • CBaseClass::Print一个虚函数。
于 2008-12-23T13:40:48.867 回答
0

也许我错了,但我没有在您的 CBaseClass::Create() 方法中找到任何返回语句!

于 2008-12-23T14:06:45.883 回答
0

就个人而言,我认为这种设计过度使用了继承。

“我正在尝试创建一个系统,在该系统中我可以从任何基类派生一个子类,并且它的实现应该替换基类的实现。” - 我不知道 IS-A 关系应该那么灵活。

我想知道您是否最好使用接口(C++ 中的纯虚拟类)和混合行为。如果我用 Java 编写它,我会这样做:

public interface Foo
{
    void doSomething();
}

public class MixinDemo implements Foo
{
    private Foo mixin;

    public MixinDemo(Foo f)
    {
        this.mixin = f;
    }

    public void doSomething() { this.mixin.doSomething(); }
}

现在我可以通过更改传递给 MixinDemo 的 Foo 实现来根据需要更改行为。

于 2008-12-23T14:21:54.433 回答