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