More C++ Idioms/查詢能力

維基教科書,自由的教學讀本

查詢能力(Capability Query)
[編輯]

目的[編輯]

在執行期間,檢查是否物件支持一個介面

別名[編輯]

動機[編輯]

從實作分離出介面是一個好的物件導向設計練習。在C++,介面類別慣用語(Interface Class idiom)被用於從實作分離出介面,而且藉由執行期間多型(polymorphism)喚起任意抽象類(abstraction) 的公用的成員函數。擴展介面類別慣用語的範例,一個具象的類別(concrete class)可能繼承多個介面,如以下所示。

class Shape { // 介面類別
   public:
    virtual ~Shape();
    virtual void draw() const = 0;
    //...
};
class Rollable { // 又一個介面類別
  public:
    virtual ~Rollable();
    virtual void roll() = 0;
};
class Circle : public Shape, public Rollable { // circles roll - 具象的類別
    //...
    void draw() const;
    void roll();
    //...
};
class Square : public Shape { // squares 不能 roll - 具象的類別
    //...
    void draw() const;
    //...
};

現在假設我們得到抽象類別的指標容器,我們在每個指標能簡單喚起函數roll(),就如同介面慣用語所描述。

std::vector<Rollable *> rollables;
// 以某種方法去填補指標向量容器。
for (vector<Rollable *>::iterator iter (rollables.begin());
     iter != rollables.end();
     ++iter)
{
  iter->roll();
}

有時候,不可能事先知道物件是否有實作一個特定介面。這樣的情況普遍發生在一個物件有繼承多個介面類別。使用查詢能力慣用語是為了知道在執行期間介面存在與否。

解決方案與範例程式[編輯]

在C++ ,查詢能力(Capability Query)典型被表示在不相關的資料型態之間一個動態型別轉換dynamic_cast。 ( if you're using w/o RTTI, dynamic_cast to derived class may have a compile error)

Shape *s = getSomeShape();
if (Rollable *roller = dynamic_cast<Rollable *>(s))
  roller->roll();

使用dynamic_cast經常被稱作跨型別轉換(cross-cast),因為它企圖轉換透過階層,而不是往上或往下到一個階層。 在此範例中ShapeRollable的階層,使用dynamic_cast 轉換型別到 Rollable 將只成功在Circle,而不是Square。因為,Square並沒有繼承介面類別 Rollable

查詢能力(Capability Query)過度使用經常是物件導向設計不良的跡象。

已知應用[編輯]

Acyclic Visitor Pattern - Robert C. Martin.

相關的慣用語[編輯]

參考[編輯]

Capability Queries - C++ Common Knowledge by Stephen C. Dewhurst