程序员很容易发现臃肿的界面,并且通常会随身携带各种各样的“刀和刺武器”来应对这种情况。 之前的一篇文章 介绍了一个界面效率方程,并演示了一个算法——由这个方程驱动——来指导这种屠杀。
然而,一个更棘手的问题是,当接口系列的成员被削减得如此之小以至于巧妙的重新组合可能会提供设计优势时。
换句话说:如果一组小接口自发地合并回一个大接口,而程序员不得不拆分那个大接口,那么相同的小接口会重新出现吗?如果他们这样做了,那么这些小接口就保留了它们的独立性。如果他们不这样做,那么这可能表明接口过度分离以及这些接口之间不合理的行为分配。
让我们看一下最近审查的程序 apache lucene ,看看它的接口在被视为相关集合时是如何成功隔离的。在这里,我们假设同一包内的接口是“相关的”。
图1是lucene的 org.apache.lucene.search.spans 包中的6个接口,一共包含了25个方法(本次分析不区分接口和抽象类)。
图 1:lucene 的 span 包中的接口。
我们将把所有这些方法收集到一个接口中,并完全基于客观的接口效率计算来分解该接口。
(回想一下,如果类 a 是接口 i 的客户端,并且 i 有 10 个方法,其中 a 调用了 10 个,那么 i 相对于 a 的效率是 100%。如果 a 只使用其中的 3 个方法,那么 i 只有 30%高效。如果第二个类 b 使用了 6 个方法,那么 i 的效率是两个客户端的平均值 = (30% + 60%) / 2 = 45%.)
图 2 显示了使用 上一篇文章 中介绍的算法在新分离的接口之间产生的方法的假设重新分配。
图 2:重新想象的 lucene 的 span 包接口。
图 2 中重新分配的接口在很大程度上保留了它们的完整性,只有一个消失了。最大的影响是将接口 connectionspans 和 span 合并为接口 2 ,这表明客户端同时使用这两个接口,但将这些接口分开似乎没有什么错误,如图 1 所示。因此,这些接口证明了它们当前的配置。
然而,如果我们看另一个 lucene 包,我们会看到一个不同的故事。包 org.apache.lucene.analysis.tokenattributes 包含23个方法的9个接口,见图3。
图 3:lucene 的 tokenattributes 包中的接口。
如果组合图 3 的接口,然后使用我们的算法将这个大接口拆分为一个有效的集合,我们将得到图 4。
图 4:重新想象的 lucene 的 tokenattributes 包接口。
图 4 已将集合从 9 个接口减少到仅 4 个接口。接口 1 主要包含 chartermattribute 接口和少量添加,接口 3 是两个小接口的组合。然而,接口 2 已将 4 个完整接口合并为一个,这表明 - 仅从效率的角度来看 - 接口集合值得进一步研究。
当然,程序员分离接口的原因不仅仅是接口效率:可能是较小的接口反映了可以以各种形式组合的不同实现,或者它们的语义不同证明了分离的合理性。
此外,这只是静态代码分析,而静态分析永远不会回答设计问题:它们只会提出问题。然而,这里提出的问题很明确:是什么促使在当前代码库中拆分接口 2 的方法?
概括
接口隔离原则 建议不要将大型接口分解为较小的接口,而是将大型 低效 接口分解为较小的高效接口。如果所有 20 个客户端类都调用一个接口的所有 60 个方法(不可否认,这在现代软件系统中很少见),那么该接口设计得很好,不应该被分解。
小接口是一种务实的妥协,但最大效率的大接口才是目标。
过度分离的界面可能会导致界面碎片更多地影响云计算,而不是澄清设计意图。