[英]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.