接口优于抽象类

接口是定义混合类型(mixin)的理想选择。 一般来说,mixin 是一个类,除了它的“主类型”之外,
还可以声明它提供了一些可选的行为。 例如, Comparable 是一个类型接口,它允许一个类声明它的
实例相对于其他可相互比较的对象是有序的。 这样的接口被称为类型,因为它允许可选功能被“混合”到
类型的主要功能。 抽象类不能用于定义混合类,这是因为它们不能被加载到现有的类中:一个类不能有
多个父类,并且在类层次结构中没有合理的位置来插入一个类型。
接口允许构建非层级类型的框架。 类型层级对于组织某些事物来说是很好的,但是其他的事物并不
是整齐地落入严格的层级结构中。 例如,假设我们有一个代表歌手的接口,和另一个代表作曲家的接
口:

public interface Singer {
AudioClip sing(Song s);
} p
ublic interface Songwriter {
Song compose(int chartPosition);
}

  在现实生活中,一些歌手也是作曲家。 因为我们使用接口而不是抽象类来定义这些类型,所以单个
类实现歌手和作曲家两个接口是完全允许的。 事实上,我们可以定义一个继承歌手和作曲家的第三个接
口,并添加适合于这个组合的新方法

public interface SingerSongwriter extends Singer, Songwriter {
AudioClip strum();
void actSensitive();
}

  你并不总是需要这种灵活性,但是当你这样做的时候,接口是一个救星。 另一种方法是对于每个受
支持的属性组合,包含一个单独的类的臃肿类层级结构。 如果类型系统中有 n 个属性,则可能需要支持
2n 种可能的组合。 这就是所谓的组合爆炸(combinatorial explosion

接口通过包装类模式确保安全的,强大的功能增强成为可能(详见第 18 条)。 如果使用抽象类来
定义类型,那么就让程序员想要添加功能,只能继承。 生成的类比包装类更弱,更脆弱

使用默认方法可以提供实现帮助多多少少是有些限制的。 尽管许多接口指定了 Object 类中方法
(如 equals hashCode )的行为,但不允许为它们提供默认方法。 此外,接口不允许包含实例
属性或非公共静态成员(私有静态方法除外)。 最后,不能将默认方法添加到不受控制的接口中


但是,你可以通过提供一个抽象的骨架实现类(abstract skeletal implementation class)来与接口一
起使用,将接口和抽象类的优点结合起来。 接口定义了类型,可能提供了一些默认的方法,而骨架实现
类在原始接口方法的顶层实现了剩余的非原始接口方法。 继承骨架实现需要大部分的工作来实现一个接
口。 这就是模板方法设计模式[Gamma95]

按照惯例,骨架实现类被称为 AbstractInterface ,其中 Interface 是它们实现的接口的名
称。 例如,集合框架( Collections Framework)提供了一个框架实现以配合每个主要集合接口:
AbstractCollection AbstractSet AbstractList AbstractMap 。 可以说,将它们称
SkeletalCollection SkeletalSet SkeletalList SkeletalMap 是有道理的,但
是现在已经确立了抽象约定。 如果设计得当,骨架实现(无论是单独的抽象类还是仅由接口上的默认方
法组成)可以使程序员非常容易地提供他们自己的接口实现。

骨架实现类的优点在于,它们提供抽象类的所有实现的帮助,而不会强加抽象类作为类型定义时的
严格约束。对于具有骨架实现类的接口的大多数实现者来说,继承这个类是显而易见的选择,但它不是
必需的。如果一个类不能继承骨架的实现,这个类可以直接实现接口。该类仍然受益于接口本身的任何
默认方法。此外,骨架实现类仍然可以协助接口的实现。实现接口的类可以将接口方法的调用转发给继
承骨架实现的私有内部类的包含实例。这种被称为模拟多重继承的技术与条目 18 讨论的包装类模式密
切相关。它提供了多重继承的许多好处,同时避免了缺陷

猜你喜欢

转载自www.cnblogs.com/lIllIll/p/12695497.html