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),因為它企圖轉換透過階層,而不是往上或往下到一個階層。 在此範例中Shape
和Rollable
的階層,使用dynamic_cast
轉換型別到 Rollable
將只成功在Circle
,而不是Square
。因為,Square
並沒有繼承介面類別 Rollable
。
查詢能力(Capability Query)過度使用經常是物件導向設計不良的跡象。
已知應用
编辑Acyclic Visitor Pattern - Robert C. Martin.
相關的慣用語
编辑參考
编辑Capability Queries - C++ Common Knowledge by Stephen C. Dewhurst