我理解composition over inheritance的优点。其中,它使单元测试(和模拟)更容易,你的代码没有与基类等耦合。我还看了nice talks关于可测试的,干净的代码,通过优秀的图片成功地将想法牢记在我的脑海中像这样:
I tried帖子解释了抽象类的含义,但我仍然不清楚:因为我可以通过接口实现多态行为,我可以将任务委托给我的依赖项通过组合,我应该在哪里使用抽象类,甚至是基础具体类?
答案 0 :(得分:1)
如果您取消了抽象类,那么如何以可重用的方式实现接口的一部分?您将创建一个接口和一些辅助类来完成您可以委派的实现,但是这个辅助类不会实现接口,因此不清楚它是否与接口相关。你会分开太多。
或者,您可以让人们完全实现接口,以便其他人知道它有一个基本实现,但是他们必须做一些黑客来指示某些方法必须被覆盖(例如,当他们被抛出异常时被称为)。
抽象类提供了一种干净的方式来提供接口以及编译器和其他工具可见的接口的部分实现。
答案 1 :(得分:1)
一个优点是便利性。抽象类可以提供默认实现。您只能覆盖所需的方法,而无需实现所有其他方法。
例如,Java的MouseListener接口有五种方法;而抽象类MouseAdapter提供了那些的默认实现。基于扩展抽象类MouseAdapter的实现只需要实现所需的方法。
另一个优点是超类方法调用可以调用子类方法。例如,C ++非虚拟习语使用它来允许超类方法在委托给子类方法之前和之后强制执行方法契约。
答案 2 :(得分:0)
抽象类对于需要共享实现的情况仍然非常有用,而不是 interface (在一般意义上)。
例如,考虑template method design pattern。这是共享实现的一个示例。算法的大部分是在抽象基类中实现的,但某些部分是由子类提供的。这些子类只需扩展其基类并在其中实现抽象模板方法即可完成工作。
将此与strategy pattern进行对比,其中整个算法被抽象化,实现被批量注入需要使用委托/组合的类中。这是共享接口的示例。虽然算法本身可能完全不同,但它们都实现了相同的基本操作。
答案 3 :(得分:0)
即使您使用了合成,继承仍然有用。恕我直言,这明确适用于框架架构。
Facade父类
您可以使用facade类将组合包装在继承上。示例将是:
public class Parent : IService
{
public Parent(IService1 s1, IService2 s2)
{
}
}
public class Child : Parent
{
public Child()
: base(Service1, Service2)
{
}
private static IService1 Service1
{
get
{
// return Service1
}
}
private static IService2 Service2
{
get
{
// return Service2
}
}
}
Source Syntax。这个facade类,可以用作默认的功能对象,在框架级对象中很有用。然后,如果再次继承,您可以仅扩展一个方法的功能,并保持原样。
扩展第三方库
并非所有第三方库都使用依赖注入。为了扩展功能,您经常需要继承它(例如:System.Windows.Forms.Form
)。特别是在自定义框架级别(企业)中,最好继承它。如果框架发生变化,那么除了被覆盖之外的其他方法也会被更改,而不是使用组合。它可能是双刃剑。