繁体   English   中英

混合类型容器与单选原则

[英]Container with mixed types vs. single choice principle

我正在考虑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在内部将所有对象存储在一个空间数据结构(八叉树)中,该结构当然仅将它们视为Pickable实例。 然后,所有被拾取线击中的对象都将包裹在Hit实例中:

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;
};

但是有些操作只能应用于实体,而某些操作只能应用于画笔,依此类推。 我看到的唯一解决方案是将所有操作移到Object接口中,为并非适用于所有对象类型的操作提供空的默认实现。 但这也感觉不对,因为这会使Object接口大大膨胀。

我的问题是,这是仅有的两个选择,还是我错过了什么? 我真的想尽可能地避免进行类型转换和类型检查,但是在这种情况下,我看不出有什么好的方法。

您可以使用访客模式

就像是:

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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM