C++ 混合型容器与单一选择原则

C++ 混合型容器与单一选择原则,c++,oop,C++,Oop,我正在思考我的3D工具拾取机制的设计。在该工具中,有几个具有不同功能的可拾取对象,这意味着我必须在UI中区分它们(根据用户拾取的内容显示不同的选项板等) 拾取机制基本上是一个容器,它知道文档中的所有对象,并且可以回答被拾取光线击中的对象的查询。它返回点击列表,按点击到相机的距离排序。要向选择器注册对象,它必须实现Pickable接口: class Pickable { typedef enum { Entity, Brush, Patch

我正在思考我的3D工具拾取机制的设计。在该工具中,有几个具有不同功能的可拾取对象,这意味着我必须在UI中区分它们(根据用户拾取的内容显示不同的选项板等)

拾取机制基本上是一个容器,它知道文档中的所有对象,并且可以回答被拾取光线击中的对象的查询。它返回点击列表,按点击到相机的距离排序。要向选择器注册对象,它必须实现
Pickable
接口:

class Pickable {
    typedef enum {
        Entity,
        Brush,
        Patch
    } Type;

    virtual Type getType() const = 0;

    virtual const BBox3& getBounds() const = 0;

    // Returns the distance of the intersection point with the given ray
    // or NaN if this object doesn't intersect with the given ray.
    virtual double intersects(const Ray3& ray) const = 0;
};
选择器在内部将所有对象存储在空间数据结构(八叉树)中,当然,八叉树只知道它们是
Pickable
的实例。然后,拾取光线击中的所有对象都将被包裹在
命中的实例中:

class Hit {
    double getDistance() const;
    const Vec3& getHitPoint() const;
    Pickable* getObject() const;
};
然后将其添加到向量并按距离排序。现在如果我想对命中的物体做些什么,我必须沿着

Hit hit = ... // obtain a hit from the picker
Pickable* object = hit.getObject();
switch (object->getType()) {
    case Pickable::Entity:
        // cast to Entity and perform some operation
        break;
    case Pickable::Brush:
        // cast to Brush and perform some operation
       break;
    ...
}
这当然违反了法律。如果我添加了一个新的对象类型,我必须接触所有散布在代码库中的switch语句。对于我想应用于这些对象的一些操作,我可以使用一个通用的超级接口,例如

class Object {
    virtual void transform(const Mat4x4& transformation);
    // other operations which are applicable to any type of object

    // I could even move some UI related functions into this interface:
    Palette* getUIPalette() const;
    void populateUIPalette(Palette* palette) const;
};
但有些操作只能应用于实体,有些操作只能应用于笔刷,等等。我看到的唯一解决方案是将所有操作移动到
对象
界面,为不适用于所有对象类型的操作提供空的默认实现。但这也感觉不正确,因为这会大大膨胀
对象
接口

我的问题是,这是唯一的两个选择还是我遗漏了什么?我真的希望尽可能避免强制转换和类型检查,但在这种情况下,我看不出有什么好办法。

您可以使用:

比如:

class IPickableVisitor;

class Pickable {
public:

    virtual void accept(IPickableVisitor& t);
};

class Entity : public Pickable {
public:
    void accept(IPickableVisitor& t) override { t.visit(*this); }
};

class Brush : public Pickable {
public:
    void accept(IPickableVisitor& t) override { t.visit(*this); }
};

class IPickableVisitor
{
public:
    virtual void visit(Entity& entity) = 0;
    virtual void visit(Brush& brush) = 0;
};
现在你可以写:

class HitPickableVisitor : public IPickableVisitor
{
public:
    virtual void visit(Entity& entity) override
    {
        // Do the entity hit code.
    }
    virtual void visit(Brush& brush) override
    {
        // Do the brush hit code.
    }
};
后者:

Pickable* object = hit.getObject();
HitPickableVisitor hitPickableVisitor;
object->accept(hitPickableVisitor);

谢谢,这是一个有趣的想法。当我添加一个新的对象类型并且没有通过添加适当的访问方法来调整所有访问者时,它会删除强制转换,编译器会抱怨。但我必须考虑这与单选原则的关系;-)我认为在从混合容器中检索对象时,没有更好的方法可以避免强制转换,因此我将此标记为答案。再次感谢!