跟踪实用程序类

我最近对我在项目代码库中看到的一个问题越来越感到沮丧。

我正在开发一个大型java项目,该项目具有>1M行代码。接口和类结构设计得非常好,编写代码的工程师非常精通。问题在于,为了让代码更干净,每当人们需要重用某些功能时,他们就会编写Utility类,因此随着时间的推移,随着项目的发展,越来越多的实用程序方法出现。但是,当下一位工程师遇到对相同功能的需求时,他无法知道某人已经在代码中的某个地方实现了实用程序类(或方法),并在另一个类中实现了该功能的另一个副本。结果是大量代码重复和太多具有重叠功能的实用程序类。

作为一个团队,我们是否可以实施任何工具或任何设计原则,以防止实用程序类的重复和低可见性?

示例:工程师 A 有 3 个位置需要将 XML 转换为 String,因此他编写了一个名为 XMLUtil 的实用程序类,并在其中放置了一个静态方法。工程师 B 有几个地方可以将文档序列化为各种格式,包括 String,因此他编写了一个名为 SerializationUtil 的实用程序类,并有一个名为“字符串”的静态方法。toString(Document)serialize(Document)

请注意,这不仅仅是代码重复,因为上面示例的2个实现很可能是不同的(假设一个使用转换器API,另一个使用Xerces2-J),所以这也可以被视为一个“最佳实践”问题......

更新:我想我最好描述一下我们当前所处的环境。我们使用Hudson进行CI,Clover用于代码覆盖率,使用Checkstyle进行静态代码分析。我们使用敏捷开发,包括日常讨论和(也许不够)代码审查。我们在 .util 中定义了所有实用程序类,由于它的大小,它现在有 13 个子包,在根 (.util) 类下大约有 60 个类。我们还使用第三方库,例如大多数apache commons jars和一些组成Guava的jar。

我很肯定,如果我们让某人重构整个软件包的任务,我们可以将实用程序的数量减少一半,我想知道是否有任何工具可以降低操作成本,以及是否有任何方法可以尽可能地延迟问题再次发生。


答案 1

解决这个问题的一个好方法是开始添加更多面向对象。以您的示例为例:

示例:工程师A有3个地方需要将XML转换为字符串,因此他编写了一个名为XMLUtil的实用程序类,并在其中放置了一个静态toString(Document)方法。

解决方案是停止使用基元类型或 JVM 提供的类型(String、Integer、java.util.Date、java.w3c.Document),并将它们包装在你自己的特定于项目的类中。然后,您的 XmlDocument 类可以提供方便的 toString 方法和其他实用工具方法。您自己的 ProjectFooDate 可以包含解析和格式化方法,否则这些方法最终会出现在各种 DateUtils 类中,等等。

这样,每当您尝试对对象执行某些操作时,IDE 都会提示您使用实用程序方法。


答案 2

您的问题是一个非常常见的问题。这也是一个真正的问题,因为没有好的解决方案。

我们在这里也处于同样的情况,好吧,我会说更糟,有1300万行代码,营业额和800多名开发人员在处理代码。我们经常讨论您描述的相同问题。

第一个想法 - 您的开发人员已经使用过 - 是重构某些实用程序类中的公共代码。我们对这个解决方案的问题在于,即使是结对编程,指导和讨论,我们实在是太多了,以至于无法有效。事实上,我们在子团队中成长,人们在他们的子团队中分享知识,但知识不会在子团队之间传递。也许我们错了,但我认为在这种情况下,即使是结对编程和谈话也无济于事。

我们还有一个架构团队。该团队负责处理设计和体系结构问题,并制作我们可能需要的通用实用程序。事实上,这个团队产生了一个我们可以称之为企业框架的东西。是的,它是一个框架,有时它运行良好。该团队还负责推动最佳实践,并提高对应该做什么或不做什么,什么可用或不可用的认识。

良好的核心Java API设计是Java成功的原因之一。好的第三方开源库也很重要。即使是一个精心设计的小型API也允许提供一个真正有用的抽象,并可以帮助减少代码大小。但是你知道,制作框架和公共API与仅仅在2小时内编写一个实用程序类完全不同。它的成本非常高。一个实用程序类的初始编码花费2个小时,调试和单元测试可能需要2天。当你开始在大项目/团队上共享通用代码时,你真的做了一个API。你必须确保完美的文档,然后真正可读和可维护的代码。发布此代码的新版本时,必须保持向后兼容。你必须在公司范围内(或至少在团队范围内)推广它。从小型实用程序类的2天,您增加到10天,20天甚至50天,用于成熟的API。

而且您的 API 设计可能并不那么出色。好吧,这并不是说你的工程师不聪明 - 事实上他们是。但是,您是否愿意让他们在一个小型实用程序类上工作50天,该类仅有助于以一致的方式解析UI的数字?当你开始使用具有完全不同需求的移动UI时,你是否愿意让他们重新设计整个事情?另外,您是否注意到世界上最聪明的工程师如何制作永远不会流行或会慢慢消失的API?你看,我们制作的第一个Web项目只使用内部框架,或者根本没有框架。然后我们添加了PHP / JSP / ASP。然后在Java中,我们添加了Struts。现在JSF是标准。我们正在考虑使用Spring Web Flow,Vaadin或Lift...

我想说的是,没有好的解决方案,开销随着代码大小和团队规模呈指数级增长。共享大型代码库会限制您的敏捷性和响应能力。任何更改都必须仔细完成,您必须考虑所有潜在的集成问题,并且每个人都必须接受新的特性和功能的培训。

但是,软件公司的主要生产力点不是在解析 XML 时获得 10 行甚至 50 行代码。无论如何,用于执行此操作的通用代码将增长到一千行代码,并重新创建一个复杂的API,该API将由实用程序类分层。当这个家伙为解析XML创建一个实用程序类时,它是很好的抽象。他为十几行甚至一百行专用代码命名。此代码很有用,因为它是专用的。通用API允许处理流,URL,字符串等。它有一个工厂,所以你可以选择你的解析器实现。实用程序类很好,因为它仅适用于此解析器和字符串。而且因为您需要一行代码来调用它。但是,当然,此实用程序代码的用途有限。它适用于此移动应用程序或加载 XML 配置。这就是为什么开发人员首先为它添加了实用程序类的原因。

总之,与其尝试整合整个代码库的代码,不如考虑随着团队的发展来划分代码责任:

  • 将从事一个大项目的大团队转变为处理多个子项目的小团队;
  • 确保接口良好,以尽量减少集成问题,但让团队拥有自己的代码;
  • 在这些团队和相应的代码库中,确保您拥有最佳实践。没有重复的代码,很好的抽象。使用社区中现有的经过验证的 API。使用结对编程,强大的API文档,维基...但是,您应该真正让不同的团队做出选择,构建自己的代码,即使这意味着跨团队的重复代码或不同的设计决策。你知道,如果设计决策不同,这可能是因为需求不同。

您真正要管理的是复杂性。最后,如果你制作一个整体式的代码库,一个非常通用和高级的代码库,你就会增加新手增加的时间,你会增加开发人员根本不使用你的通用代码的风险,并且你会减慢每个人的速度,因为任何更改都有更大的机会破坏现有功能。


推荐