Aar*_*ron 4 c++ abstract-class interface composition
我想我理解界面和抽象之间的区别.Abstract设置默认行为,在纯抽象的情况下,行为需要由派生类设置.接口是您需要的,无需基类的开销.那么界面优于作曲的优势是什么?我能想到的唯一优势是在基类中使用受保护的字段.我错过了什么?
你的标题没有意义,你的解释有点模糊,所以让我们定义术语(并介绍缺少的关键)。
这里发生了两件不同的事情:
让我们从接口和抽象类开始。
因此,在 C++ 的上下文中,两者之间没有太大区别。特别是因为这种区别从未考虑过自由函数。
例如,考虑以下“接口”:
class LessThanComparable {
public:
virtual ~LessThanComparable() {}
virtual bool less(LessThanComparable const& other) const = 0;
};
Run Code Online (Sandbox Code Playgroud)
即使使用免费功能,您也可以轻松地增加它:
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);
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我们提供行为......但类本身仍然是一个接口......哦,好吧。
因此,真正的争论是在继承和组合之间。
继承经常被误用于继承行为。这不好。应该使用继承来建模is-a关系。否则,您可能需要 Composition。
考虑一个简单的用例:
class DieselEngine { public: void start(); };
Run Code Online (Sandbox Code Playgroud)
现在,我们如何Car用这个构建一个?
如果您继承,它将起作用。然而,突然你得到这样的代码:
void start(DieselEngine& e) { e.start(); }
int main() {
Car car;
start(car);
}
Run Code Online (Sandbox Code Playgroud)
现在,如果您决定替换DieselEngine为WaterEngine,则上述功能不起作用。编译失败。并且WaterEngine继承自DieselEngine当然感觉不舒服......
那有什么解决办法呢?组成。
class Car {
public:
void start() { engine.start(); }
private:
DieselEngine engine;
};
Run Code Online (Sandbox Code Playgroud)
这样,没有人可以编写假设汽车是发动机的无意义代码(doh!)。因此,更换发动机很容易,绝对不会对客户产生影响。
这意味着您的实现和使用它的代码之间的依从性较低;或者通常所说的:减少耦合。
经验法则是,一般来说,从具有数据或实现行为的类继承应该是不受欢迎的。它可以是合法的,但通常有更好的方法。当然,就像所有的经验法则一样,它是一粒盐。小心过度设计。
接口定义了您将如何使用。
你继承是为了被重用。这意味着您想要适应某个框架。如果您不需要适应框架,即使是您自己制作的框架,也不要继承。
组合是一个实现细节。不要为了获得基类的实现而继承,组合它。仅当它允许您适应框架时才继承。
接口定义行为.抽象类有助于实现行为.
从理论上讲,完全没有实现的纯抽象类与接口之间没有太大区别.两者都定义了一个未实现的API.但是,纯抽象类通常用于不支持接口的语言,以提供类似语义的接口(例如C++).
当您有选择时,通常抽象基础将提供某种程度的功能,即使它不完整.它有助于实现常见行为.不利的一面是你被迫从中衍生出来.当您只是定义用法时,请使用界面.(没有什么能阻止你创建一个实现接口的抽象基础).