我确定我在这里缺少一些元素,但我无法理解它。
假设我们有几个可能的Manager
类实现,它们处理Base
类型的对象。应该可以定义在运行时使用哪个实现。
根据Manager
的实施,他们必须设置并获取Base
的特定属性,因此他们在内部使用的派生DerivedA
和DerivedB
。有没有办法绕过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的帖子,因为它提供了解决此问题的可能方法。然而,经过一些考虑,我发现课程设计存在潜在的问题。
答案 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
参数时,您感兴趣的只是那些抽象操作。在完全干净的面向对象设计中,至少。
你的班级设计不是干净的面向对象,这就是全部。但这本身并不是问题。如果它对您有用并且代码易于阅读和维护,那么一切都很好。