我认为我理解了接口和抽象类的区别。抽象类设置默认行为,对于纯抽象类,行为需要由派生类设置。接口是从基类中获取所需内容而无需附加开销。那么,与组合相比,接口的优势在哪里?我唯一能想到的优点是在基类中使用受保护字段。我漏掉了什么吗?
我认为我理解了接口和抽象类的区别。抽象类设置默认行为,对于纯抽象类,行为需要由派生类设置。接口是从基类中获取所需内容而无需附加开销。那么,与组合相比,接口的优势在哪里?我唯一能想到的优点是在基类中使用受保护字段。我漏掉了什么吗?
class LessThanComparable {
public:
virtual ~LessThanComparable() {}
virtual bool less(LessThanComparable const& other) const = 0;
};
您可以轻松地增强它,甚至使用免费的函数:
inline bool operator<(LessThanComparable const& left, LessThanComparable const& right) {
return left.less(right);
}
inline bool operator>(LessThanComparable const& left, LessThanComparable const& right) {
return right.less(left);
}
inline bool operator<=(LessThanComparable const& left, LessThanComparable const& right) {
return not right.less(left);
}
inline bool operator>=(LessThanComparable const& left, LessThanComparable const& right) {
return not left.less(right);
}
class DieselEngine { public: void start(); };
Car
?void start(DieselEngine& e) { e.start(); }
int main() {
Car car;
start(car);
}
WaterEngine
替换DieselEngine
,上述函数就无法使用。编译失败。而让WaterEngine
继承自DieselEngine
显然感觉很奇怪... 那么解决方案是什么呢?组合。
class Car {
public:
void start() { engine.start(); }
private:
DieselEngine engine;
};
接口定义了你的使用方式。
继承是为了重用代码。这意味着你想要适应某个框架。如果你不需要适应任何框架,甚至包括自己创建的框架,就不要继承。
组合是一个实现细节。不要为了获取基类的实现而继承它,应该使用组合。只有在需要适应框架的情况下才应该继承。
接口是轻量级的,用C++可以描述为只有纯虚函数的类。轻量级是好的,因为
这个特点与动态库链接结合使用,有助于促进“即插即用”,这是近年来未被赞扬但伟大的软件创新之一。这导致更大的软件互操作性、可扩展性等。
接口可能需要更多的工作来实现。当您有一个重要的子系统可能有多个可能的实现时,通过接口使用该子系统应该被采用。
通过继承进行重用需要更多关于您正在覆盖的实现行为的知识,因此存在更大的“耦合”。尽管如此,在接口过度的情况下,这也是一种有效的方法。