2012-04-26 31 views
4

我想我理解界面和抽象之间的区别。抽象设置默认行为,在纯抽象的情况下,行为需要由派生类设置。接口是一个你需要的东西,而不需要基类的开销。那么界面相对于构图的优点是什么?我能想到的唯一优点是在基类中使用受保护的字段。我错过了什么?界面vs构图

+2

您正在考虑使用哪种语言/ – 2012-04-26 13:56:07

回答

3

一个接口定义了你将如何使用。

您继承以便重用。这意味着你想要适应一些框架。如果你不需要适应框架,即使你自己制作一个框架,也不要继承。

构图是实现细节。为了获得基类的实现,不要继承,编写它。只有在允许您适合框架的情况下才能继承。

5

接口定义了行为。抽象类有助于实现行为。

从理论上讲,纯粹的抽象类没有任何实现和接口之间没有太大的区别。两者都定义了未实现的API。然而,纯粹的抽象类经常被用在不支持提供接口(如语义)的接口的语言中(如C++)。

当你有选择时,通常抽象基础将提供某种程度的功能,即使它不完整。它有助于实现共同的行为。不利的一面是你被迫从中得出结论。在简单定义使用情况时,请使用界面。 (没有什么能阻止你创建一个实现接口的抽象基础)。

0

接口很薄,在C++中它们可以被描述为只有纯虚函数的类。薄是好事,因为

  • 它减少了在使用或实现的接口
  • 它减少了用户与接口的实现之间的耦合(依赖)的学习曲线。因此,用户可以很好地隔离他们正在使用的界面的实施变化。

这与动态库链接的同时,有助于促进即插即用,最近一个时期的无名英雄,但伟大的软件创新之一。这导致更大的软件互操作性,可扩展性等。

接口可以做更多的工作。当你有一个重要的子系统有一天可能有多个可能的实现时,证明它们的采用是合理的。子系统在这种情况下应该通过接口使用。

通过不合理的方式重复使用需要更多地了解您所覆盖的实施行为,因此存在更大的“耦合”。这就是说,在接口过度消耗的情况下,这也是一种有效的方法。

+0

在Interface vs Composition的特定上下文中,接口引入* more *耦合而不是composition。 – 2012-04-26 14:45:08

+0

我不明白。随意阐述。 – ScrollerBlaster 2012-04-26 14:50:45

+1

问题是关于继承行为。问题是继承是从外部可见的,因此其他人可能会使用它,引入不必要的耦合。另一方面,由于您的属性是隐藏的,因此没有人依赖它们,因此Composition不会引入任何耦合。 – 2012-04-26 15:07:13

4

您的标题没有意义,而且您的解释有点模糊,所以让我们定义术语(并介绍缺失的关键字)。

有两种不同的东西怎么回事:

  • 抽象类V5接口
  • 继承VS组成

让我们先从接口和抽象类。

  • 一种抽象类(在C++中)是不能被实例化,因为至少有一个其方法是一个纯虚拟方法的类。
  • 一个接口是一种没有实现的方法,在C++中它是模拟抽象类只有纯虚拟方法。

因此,在C++的情况下,两者之间没有太大的区别。特别是因为这个区别从来没有考虑到自由功能。

例如,请考虑下面的“接口”:

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); 
} 

在这种情况下,我们提供的行为......但该类本身仍然是一个接口......哦,好吧。


真正的辩论,因此,是组成继承和之间

继承经常被误用于继承行为。这不好。应该使用继承来建模is-a的关系。否则,你可能想要组合。

考虑简单的例子:

class DieselEngine { public: void start(); }; 

现在,我们如何建立与这个Car

如果你继承,它将工作。但是,突然间你会得到这样的代码:

void start(DieselEngine& e) { e.start(); } 

int main() { 
    Car car; 
    start(car); 
} 

现在,如果你决定WaterEngine更换DieselEngine,上述功能不起作用。编译失败。并有WaterEngine继承DieselEngine肯定会感到ikky ...

然后是什么解决方案? 组成

class Car { 
public: 
    void start() { engine.start(); } 

private: 
    DieselEngine engine; 
}; 

这样,没有人可以写一个假设一个汽车发动机荒谬的代码(DOH!)。因此,更换引擎是容易与绝对没有客户影响

这意味着您的实现和使用它的代码之间的依从性会降低;或者因为它通常被称为:少耦合


经验法则是,一般情况下,从具有数据或实现行为的类继承应该皱眉。这可能是合法的,但通常有更好的方法。当然,就像所有的经验法则一样,它应该用一粒盐来进行。注意过度工程。

0

如果类型Y继承自类型X,那么知道如何处理类型X的对象的代码在大多数情况下会自动处理Y类型的对象。同样,如果类型Z实现接口I,那么知道如何使用实现I的对象的代码无需了解任何关于它们的任何信息就能自动使用Z类型的对象。继承和接口的主要目的是允许这样的替换。相反,如果P类型的对象包含Q类型的对象,那么期望与Q类型对象一起工作的代码将无法在P类型中工作(除非P继承自Q持有该类型的对象)。只要P的代码直接将其提供给该代码或使其可用于外部代码,那么期望操作Q类型对象的代码将能够对P中包含的Q实例进行操作。