2014-02-07 49 views
1

我在思考我的3D工具採摘機制設計。在這個工具中,有幾個可選對象具有不同的功能,這意味着我必須爲它們區分它們(根據用戶選擇的內容顯示不同的調色板等)。具有混合類型與單選原則的容器

拾取機制基本上是一個容器,它知道文檔中的所有對象,並且可以回答被拾取光線擊中的對象的查詢。它會返回一個命中列表,按它們到相機的距離排序。註冊到選擇器對象時,它必須實現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實例的所有對象。然後由拾取射線被擊中的所有對象都被包裹在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; 
    ... 
} 

行這當然違反了single choice principle做一些事情。如果我添加了一個新的對象類型,我必須觸及遍佈在我的代碼庫中的所有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接口,爲不適用於所有對象類型的操作提供空的默認實現。但是這也感覺不對,因爲這會大大增加界面。

我的問題是,這些是唯一的兩個選擇,還是我錯過了什麼?我真的想盡量避免投射和檢查類型,但在這種情況下,我看不到一個好方法。

回答

1

你可以使用一個Visitor Pattern

喜歡的東西:

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

謝謝,這是一個有趣的想法。它刪除了強制轉換,當我添加一個新的對象類型並且不通過添加適當的訪問方法來調整所有訪問者時,編譯器會投訴。我不得不考慮這與單選原則是如何相關的;-) –

+0

我認爲沒有更好的方法來避免從混合容器中檢索對象時的強制轉換,所以我將其標記爲答案。再次感謝! –