通常情况下,协变性允许您在派生类接口中表达比基类接口更多的信息。派生类的行为比基类更具体,而协变性表达了(一个方面的)差异。
当您拥有相关层次结构的gubbins时,它非常有用,在某些客户端将使用基类接口,但其他客户端将使用派生类接口的情况下。
class URI { };
class HttpAddress : public URI {
bool hasQueryParam(string);
string &getQueryParam(string);
};
class Resource {
virtual URI &getIdentifier();
};
class WebPage : public Resource {
virtual HttpAddress &getIdentifier();
};
已知具有Web页面的客户端(例如浏览器)知道查看查询参数的意义。而使用Resource基类的客户端则不知道这一点。他们将始终将返回的
HttpAddress&
绑定到
URI&
变量或临时变量中。
如果他们怀疑但不确定他们的Resource对象具有HttpAddress,则可以使用
dynamic_cast
。但是,协变性比“只知道”并进行转换更优越,原因与静态类型在所有情况下都有用的原因相同。
有替代方案 - 将
getQueryParam
函数放在
URI
上,但使
hasQueryParam
对所有内容返回false(会使URI接口混乱)。将
WebPage :: getIdentifier
定义为返回
URL&
,实际上返回
HttpIdentifier&
,并让调用者执行无意义的
dynamic_cast
(会使调用代码和WebPage的文档混乱,其中您说“返回的URL保证可以动态转换为HttpAddress”)。在
WebPage
中添加一个
getHttpIdentifier
函数(会使
WebPage
接口混乱)。或者只是将covariance用于其旨在表达的事实,即
WebPage
没有
FtpAddress
或
MailtoAddress
,而是有一个
HttpAddress
。
最后当然有一个合理的观点,即您不应该有关系层次结构,更不用说相关的关系层次结构了。但是,这些类也可以是具有纯虚方法的接口,因此我不认为这影响使用covariance的有效性。