在执行代码审查时,或者在以紧急性和优先级的名义证明了快速编码的合理性之后,您可以再次到达那里:很显然,它位于另一个辅助类的面前。 但是一切工作正常,演出必须继续进行,发布后再发布,以便帮助程序类很快成为怪物类,提供大量的静态方法,在其utils包中自由增长,通常在面向对象的情况下没有技术债务设计不敢介入。 所提供的设施是集中式的,因此DRY –会叫喊一些开发人员,可能是其编码人员。 之所以快是因为所有事情都是静态的 –可能要求团队中的其他人,也许是在其列表中添加了另一种静态方法的人。 它易于使用,我们保持简单 -您可以在房间里听到,这是对KISS的另一种误解 。
我们可能会争辩说, helper和utils类通常很容易实现,特别是当我们无法修改新功能的正确目标类(例如,位于外部库中)或实际上找不到该目标时(不清楚的领域模型,PoC,缺乏要求),或者我们只是不想找到它( 懒惰 ,辅助类的主要全局来源)。 不过,最大的问题是,这显然不是面向对象的解决方案,并且到时候(由于缺乏团队沟通,资源轮换,快速修复和变通办法),它可能导致无休止的静态方法容器和麻烦的维护工作(您想变干) ,但是最终有十种方法提供几乎相同的功能(如果不是相同的话);您想提高速度,但是现在您不能轻松地在该静态怪兽中添加缓存机制,否则会遇到并发麻烦;您想要为简化起见,但您的IDE现在提供了一长串的异构方法,这并不能简化您的任务)。 但是不用担心,我们将尽力解决它。
让我们重构该助手类
首先,我们需要定义我们的目标问题: 无状态类(带有特殊的Helper或Utils后缀),该类仅提供静态方法,从不实例化为项目中的对象,而没有明确的责任 。
第二,我们需要一种几乎确定性的方法来解决问题。 这几乎代表着例外和项目的特殊性:最终的决定实际上取决于特定的方案,而这些方案只会消失所有关于通用解决方案的主张。 我们最终需要分析给定的类并尝试:
- 查找某个静态方法应属于的目标类,或者
- 查找该类实际提供的目标业务领域,然后将其转换为相关组件,将其重命名并删除静态方法(通过行为替换它们),或者
- 通过面向对象的方法添加一个提供一个或多个行为的新类(以前存在的静态方法)。
以上任何解决方案都可以为我们提供更好的模型。 然后,我们通过以下步骤(假设每个步骤都相应地重构了项目)来说明这一点:
- 为了简化我们的任务,让我们在项目中删除助手类中所有未使用的方法(您的IDE肯定会为您提供帮助)。
- 然后,将类定义设置为final 。 您在项目中是否遇到任何编译错误? 如果是,为什么扩展了该helper或utils类? 但是,您可能已经有一个目标:子类。 如果子类又是另一个帮助器类(真的吗?),请将其与父类合并。
- 如果还不存在,请向该类添加一个私有构造函数。 您在项目中是否遇到任何编译错误? 然后实际上是实例化了该类的某个地方,因此它不是纯辅助类,或者未正确使用。 查看这些调用者,您可能会发现一个方法或整个方法集可能属于的目标类(或域)。
- 让我们通过某种相似性 ,相似签名的类方法将类方法分组,将帮助器类分解为较小的帮助器类(从杂类到相关方法,亲和性可能确实是我们的目标域 )。 通常,在这一点上,我们会从大型utils类转向轻型的helper类(提示:此时不要害怕仅使用一种方法创建类), 缩小范围(从ProjectUtils到CarHelper,EngineHelper,WheelHelper等) )。 (嘿,您的代码是否已经变得更干净了?)。
- 如果这些新类中的任何一个只有一个方法,让我们检查一下它的用法。 如果我们只有一个呼叫者,那么您很幸运,那就是我们的目标人群! 您可以将方法作为行为或私有方法移至该类(保留其静态标记或利用内部状态)。 助手班不见了。
- 到目前为止,我们在每个帮助程序类中(尽管实际上可能是您的起点)都在这些相关方法之间确定了一个共同的状态 。 提示:寻找大多数这些方法都具有的公共参数 (即所有方法都将Car对象作为输入),这是一个警告,这些方法可能应作为行为属于Car类(或扩展名?包装器?)。 否则,该公共参数可能会变成一个类字段,即状态,可以将其传递给构造函数,并由所有(不再是静态的)方法使用。 该状态将提示您该类的前缀,方法相似性可能建议一个行为的类(CarValidator,CarReader,CarConverter等)。 助手班不见了。
- 如果这一系列方法使用不同的参数,具体取决于可选输入或同一输入的表示形式,请考虑使用Builder模式通过流畅的界面来转换Helper:从一组静态方法(如Helper.calculate(x) , calculate( x,y) , calculate(x,z) , calculate(y,z)我们可以很容易地找到newBuilder()。with(x).with(y).calculate()之类的东西 。 然后,帮助程序类将提供行为 ,减少其业务方法列表,并为将来的扩展提供更大的灵活性。 然后,调用者将其用作内部字段以进行重用或在需要时实例化它。 帮助类(我们知道)消失了。
- 如果helper类提供的方法实际上是针对不同输入的操作(但是,此时,对于同一域),请考虑应用Command模式 :调用方实际上将创建所需的命令(它将处理必要的输入并提供一个行为),调用者将在特定上下文中执行它。 您可能会为每个静态方法获得一个命令实现,并且您的代码将从Helper.calculate(x,y) , calculate(z)移至invoker.calculate(new Action(x,y))之类 。 再见帮手课。
- 如果帮助程序类提供的方法用于相同的输入但逻辑不同,请考虑应用策略模式 :每个静态方法都可以轻松地成为策略实现,从而消除了其原始帮助程序类的需要(然后由上下文组件替换)。
- 如果给定的静态方法集涉及某个类层次结构或定义的组件集合,则可以考虑应用Visitor模式 :您可能会获得几个提供不同访问方法的visitor实现,这些访问方法可能会部分或完全替代以前存在的静态方法。
- 如果以上情况均不符合您的标准,请应用三个最重要的指标:您的经验,在给定项目中的能力和常识。
结论
该过程非常清晰,寻找合适的域和合理的目标类,或者考虑通过应用面向对象设计的标准方法来重构给定的帮助器类(尽管在某些情况下增加了代码复杂性,这值得吗?)。 在前面提到的案例列表中,一个铃铛可能会在尝试了解如何实现这一点(通常不太容易)的过程中为您提供帮助。 具体的限制可能会限制某些解决方案; 静态方法和所涉及流程的复杂性可能需要重构的几个阶段,并将其精炼到可接受的结果。 或者,您可以选择以某种代码可读性和简单性的名义坚持使用该助手类(希望至少应用上述前5个步骤)。 辅助类并不是普遍邪恶的,但是通常您实际上并不需要它们。
翻译自: https://www.javacodegeeks.com/2014/04/how-to-get-rid-of-helper-and-utils-classes.html