场景图更新回调设计
Scene Graph Update Callback Design
所以我使用 Open Scene graph 创建一个应用程序,并且我有一个从 OSG 的回调 class 扩展而来的回调 class。它只是在场景图节点的更新事件遍历中调用每个帧的回调。
我需要不同的回调 classes 对它们所连接的节点执行不同的操作。所以我有一个基本回调 class 我称之为 controller base:
class ControllerBase : public osg::NodeCallback
{
public:
ControllerBase();
private:
// operator is overridden here from NodeCallback and is called each frame.
virtual void operator()(osg::Node * n, osg::NodeVisitor * nv);
}
operator() 给我回调也附加的节点和节点访问者。现在取决于节点,它可能是不同的 class,例如转换或开关。
所以我需要进行动态转换,但对于节点可能的每种可能类型。所以:
class ControllerBase : public osg::NodeCallback
{
public:
ControllerBase();
private:
virtual void operator()(osg::Node * n, osg::NodeVisitor * nv)
{
Type1 * t1 = dynamic_cast<Type1*>(node);
Type2 * t2 = dynamic_cast<Type1*>(node);
Type3 * t3 = dynamic_cast<Type1*>(node);
}
}
然后通过虚拟方法将它们发送出去,由我将附加到节点的特定控制器 class 继承。
class ControllerBase : public osg::NodeCallback
{
public:
ControllerBase();
protected:
private:
virtual void On_Frame(Type1*, osg::NodeVisitor) = 0;
virtual void On_Frame(Type2*, osg::NodeVisitor) = 0;
virtual void On_Frame(Type3*, osg::NodeVisitor) = 0;
virtual void operator()(osg::Node * n, osg::NodeVisitor * nv)
{
Type1 * t1 = dynamic_cast<Type1*>(node);
Type2 * t2 = dynamic_cast<Type1*>(node);
Type3 * t3 = dynamic_cast<Type1*>(node);
if(t1)
On_Frame(t1, nv);
if(t2)
On_Frame(t2, nv);
if(t3)
On_Frame(t3, nv);
}
}
class Type1_Controller
{
public:
Type1_Controler();
private:
virtual void On_Frame(Type1 * type, osg::NodeVisitor *nv) override
{
// Do type 1 related stuff here.
}
virtual void On_Frame(Type2 * type, osg::NodeVisitor *nv) override
{
// Leave empty, not needed.
}
virtual void On_Frame(Type3 * type, osg::NodeVisitor *nv) override
{
// Leave empty, not needed.
}
}
所以现在对于我拥有的每种类型的控制器,我都必须实现剩余的空方法。这感觉像是糟糕的设计,但我想不出如何编写更好的实现。也许 3 种类型并没有那么糟糕,但随着我的进行,我可能会添加更多。我考虑过使用模板 class,但如果我没记错的话,我不能在模板 class 中使用虚拟方法。我可以只使用具有空实现的非纯虚拟方法,我想这可以在选择时被覆盖。什么是好的方法或建议?
根据您的设计,类 - Type1, Type2, Type3
似乎都派生自 osg::Node
。在这种情况下,为什么要使用 dynamic_cast 并尝试确定 operator() 重载中的类型?您可以在 ControllerBase
-
中执行此操作
virtual void operator()(osg::Node * n, osg::NodeVisitor * nv)
{
On_Frame(n, nv);
}
然后在每个 Type1_Controller
、Type2_Controller
等中都有一个 On_Frame 方法作为 -
virtual void On_Frame(osg::Node * n, osg::NodeVisitor *nv) override
{
Type1 * t1 = dynamic_cast<Type1*>(n);
if(t1)
{
// only if t1 do something
}
}
osg::NodeVisitor 或多或少是访问者设计模式的教科书式实现。 (有关原始 "Gang of Four" 或 GoF 书籍的更多信息,请参阅 http://en.wikipedia.org/wiki/Design_Patterns)。
您可以覆盖 t1 节点中的 accept(NodeVisitor) class 以尝试转换为您的 t1 访问者类型,例如:
Type1_Visitor* vis = dynamic_cast<NodeVisitor> nv;
if(vis)
vis->Type1Apply(*this);
else
nv->apply(*this);
当然,还有更多内容,但这是 GoF 书中阐述的双重调度的一般思想。
所以我使用 Open Scene graph 创建一个应用程序,并且我有一个从 OSG 的回调 class 扩展而来的回调 class。它只是在场景图节点的更新事件遍历中调用每个帧的回调。
我需要不同的回调 classes 对它们所连接的节点执行不同的操作。所以我有一个基本回调 class 我称之为 controller base:
class ControllerBase : public osg::NodeCallback
{
public:
ControllerBase();
private:
// operator is overridden here from NodeCallback and is called each frame.
virtual void operator()(osg::Node * n, osg::NodeVisitor * nv);
}
operator() 给我回调也附加的节点和节点访问者。现在取决于节点,它可能是不同的 class,例如转换或开关。
所以我需要进行动态转换,但对于节点可能的每种可能类型。所以:
class ControllerBase : public osg::NodeCallback
{
public:
ControllerBase();
private:
virtual void operator()(osg::Node * n, osg::NodeVisitor * nv)
{
Type1 * t1 = dynamic_cast<Type1*>(node);
Type2 * t2 = dynamic_cast<Type1*>(node);
Type3 * t3 = dynamic_cast<Type1*>(node);
}
}
然后通过虚拟方法将它们发送出去,由我将附加到节点的特定控制器 class 继承。
class ControllerBase : public osg::NodeCallback
{
public:
ControllerBase();
protected:
private:
virtual void On_Frame(Type1*, osg::NodeVisitor) = 0;
virtual void On_Frame(Type2*, osg::NodeVisitor) = 0;
virtual void On_Frame(Type3*, osg::NodeVisitor) = 0;
virtual void operator()(osg::Node * n, osg::NodeVisitor * nv)
{
Type1 * t1 = dynamic_cast<Type1*>(node);
Type2 * t2 = dynamic_cast<Type1*>(node);
Type3 * t3 = dynamic_cast<Type1*>(node);
if(t1)
On_Frame(t1, nv);
if(t2)
On_Frame(t2, nv);
if(t3)
On_Frame(t3, nv);
}
}
class Type1_Controller
{
public:
Type1_Controler();
private:
virtual void On_Frame(Type1 * type, osg::NodeVisitor *nv) override
{
// Do type 1 related stuff here.
}
virtual void On_Frame(Type2 * type, osg::NodeVisitor *nv) override
{
// Leave empty, not needed.
}
virtual void On_Frame(Type3 * type, osg::NodeVisitor *nv) override
{
// Leave empty, not needed.
}
}
所以现在对于我拥有的每种类型的控制器,我都必须实现剩余的空方法。这感觉像是糟糕的设计,但我想不出如何编写更好的实现。也许 3 种类型并没有那么糟糕,但随着我的进行,我可能会添加更多。我考虑过使用模板 class,但如果我没记错的话,我不能在模板 class 中使用虚拟方法。我可以只使用具有空实现的非纯虚拟方法,我想这可以在选择时被覆盖。什么是好的方法或建议?
根据您的设计,类 - Type1, Type2, Type3
似乎都派生自 osg::Node
。在这种情况下,为什么要使用 dynamic_cast 并尝试确定 operator() 重载中的类型?您可以在 ControllerBase
-
virtual void operator()(osg::Node * n, osg::NodeVisitor * nv)
{
On_Frame(n, nv);
}
然后在每个 Type1_Controller
、Type2_Controller
等中都有一个 On_Frame 方法作为 -
virtual void On_Frame(osg::Node * n, osg::NodeVisitor *nv) override
{
Type1 * t1 = dynamic_cast<Type1*>(n);
if(t1)
{
// only if t1 do something
}
}
osg::NodeVisitor 或多或少是访问者设计模式的教科书式实现。 (有关原始 "Gang of Four" 或 GoF 书籍的更多信息,请参阅 http://en.wikipedia.org/wiki/Design_Patterns)。
您可以覆盖 t1 节点中的 accept(NodeVisitor) class 以尝试转换为您的 t1 访问者类型,例如:
Type1_Visitor* vis = dynamic_cast<NodeVisitor> nv;
if(vis)
vis->Type1Apply(*this);
else
nv->apply(*this);
当然,还有更多内容,但这是 GoF 书中阐述的双重调度的一般思想。