如何在具有接口和基类时避免向下转换?

时间:2014-07-27 16:32:11

标签: c++ design-patterns

我确定我在这里缺少一些元素,但我无法理解它。

假设我们有几个可能的Manager类实现,它们处理Base类型的对象。应该可以定义在运行时使用哪个实现。

根据Manager的实施,他们必须设置并获取Base的特定属性,因此他们在内部使用的派生DerivedADerivedB。有没有办法绕过Handle方法中向下转换参数的需要,以获得特定于实现的属性?

class Base { /* Abstract class with common properties */ };
class DerivedA : public Base { /* DerivedA-specific properties */ };
class DerivedB : public Base { /* DerivedB-specific properties */ };

class IManager { /* These functions must be implemented by every Manager implementation */
public:
    virtual Base* Create() = 0;
    virtual void Handle(Base*) = 0;
};

class AManager : public IManager
{
public:
    Base* Create() override { return new DerivedA(); }
    void Handle(Base* pFoo) override 
    { 
        // Now if we want to access pFoo's specific properties, we will need to dynamic_cast it
    }
};

class BManager : public IManager
{
public:
    Base* Create() override { return new DerivedB(); }
    void Handle(Base* pBar) override { /* same here */ }
};

void Run(bool useAManager)
{
    IManager* pManager = nullptr;

    if (useAManager)
        pManager = new AManager();
    else
        pManager = new BManager();

    Base* pData = pManager->Create();
    /* use Base specific properties ... */
    pManager->Handle(pData);
}

编辑:谢谢大家的宝贵意见。我将接受@ jpo38的帖子,因为它提供了解决此问题的可能方法。然而,经过一些考虑,我发现课程设计存在潜在的问题。

2 个答案:

答案 0 :(得分:2)

您可以使用visitor pattern。在您的示例中,这将是:

class DerivedA;
class DerivedB;
class Visitor
{ 
public:
    virtual void visitA( DerivedA& a ) = 0;
    virtual void visitB( DerivedB& b ) = 0;
};


class Base 
{ 
public:
    virtual void Accept( Visitor& visitor ) = 0;
};

class DerivedA : public Base 
{ 
public:
    virtual void Accept( Visitor& visitor ) { visitor.visitA( *this ); }
};

class DerivedB : public Base 
{ 
public:
    virtual void Accept( Visitor& visitor ) { visitor.visitB( *this ); }
};

然后,来自AManager或BManager:

void Handle(Base* pFoo) 
{ 
    class MyVisitor : public Visitor
    {
    public:
        virtual void visitA( DerivedA& a )
        {
             // do somethiong specific to a, you have access to DerivedA
        }
        virtual void visitB( DerivedB& b )
        {
             // do somethiong specific to b, you have access to DerivedB
        }
    };
    MyVisitor v;
    pFoo->Accept( v );
}

访问者模式的缺点是,每当您想要做某些特定事情时,您都必须定义一个新的访问者类。

您也可以考虑这样做(但我绝对推荐访问者,如果您稍后添加DerivedC或希望通过共享访问者类别共享某些特定操作,则非常有帮助。)

class Base 
{ 
public:
    virtual DerivedA* GetAsA() = 0;
    virtual DerivedB* GetAsB() = 0;
};

class DerivedA : public Base 
{ 
public:
    virtual DerivedA* GetAsA() { return this; }
    virtual DerivedB* GetAsB() { return NULL; }
};

class DerivedB : public Base 
{ 
public:
    virtual DerivedA* GetAsA() { return NULL; }
    virtual DerivedB* GetAsB() { return this; }
};

然后,来自AManager或BManager:

void Handle(Base* pFoo) 
{ 
    if ( pFoo->GetAsA() )
    {
        // use GetAsA to access DerivedA object avoiding downcasting
    }
    if ( pFoo->GetAsB() )
    {
        // use GetAsB to access DerivedB object avoiding downcasting
    }
}

答案 1 :(得分:0)

不是真的。如果您绝对需要以不同的方式处理特定的子类型,dynamic_cast是最干净的解决方案。

严格地说,这里的真正问题始于"属性"。面向对象的基类没有属性,只有操作,当您接受Base参数时,您感兴趣的只是那些抽象操作。在完全干净的面向对象设计中,至少。

你的班级设计不是干净的面向对象,这就是全部。但这本身并不是问题。如果它对您有用并且代码易于阅读和维护,那么一切都很好。