我了解组合优于继承的优势。除其他外,它使单元测试(和模拟)更容易,您的代码不与基类耦合等。我还观看了关于可测试、干净代码的精彩演讲,通过这样的优秀图片成功地将这些想法留在了我的脑海中:
我尝试了解释抽象类含义的帖子,但我仍然不清楚:由于我可以通过接口实现多态行为,并且我可以通过组合将任务委托给我的依赖项,我应该在哪里使用抽象类,甚至是基础具体上课?
一个优点是方便。抽象类可以提供默认实现。您可以只覆盖您想要的方法,而根本不需要实现所有其他方法。
例如,Java 的 MouseListener 接口有五个方法;抽象类 MouseAdapter 提供了这些的默认实现。基于扩展抽象类 MouseAdapter 的实现只需要实现所需的方法。
另一个优点是超类方法调用可以调用子类方法。例如,C++ 非虚拟习语使用它来允许超类方法在委托给子类方法之前和之后强制执行方法协定。
如果您取消了抽象类,您将如何以可重用的方式实现接口的一部分?您将创建一个接口和一些辅助类来执行您可以委托给的实现,但是这个辅助类不会实现该接口,因此不清楚它是否与该接口相关。你会解耦太多。
或者,您会让人们完全实现接口,以便其他人知道它具有基本实现,但随后他们将不得不做一些黑客行为以指示必须覆盖某些方法(例如,在调用它们时抛出异常) .
抽象类提供了一种简洁的方式来提供接口以及对编译器和其他工具可见的接口的部分实现。
即使您使用了组合,继承仍然有用。恕我直言,这明确适用于框架架构。
门面父类
您可以使用外观类将您的组合包装在继承上。示例将是:
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
}
}
}
源语法。这个外观类,可以用作默认功能对象,在框架级对象中很有用。然后,如果再次继承,您可以只扩展一种方法的功能,其余的保持原样。
扩展第三方库
并非所有第三方库都使用依赖注入。为了扩展功能,您经常需要继承它(例如:)System.Windows.Forms.Form
。尤其是自定义Framework级别(企业级),最好继承。如果框架发生变化,您覆盖的其他方法也将被更改,而不是使用组合。虽然它可能是双刃剑。