如何摆脱工具类

不管是进行代码review仍是紧急编码调整,你总会发现:你又搞出了一个帮助类(helper class)。代码运行一切正常,进度又必须跟上,发布任务一个接一个,所以那个帮助类逐渐变成了一个提供了不少静态(static)方法的“怪兽类”(monster class),在它的utils包内不受控制地增加。utils包长久以来就是一个技术争议的荒蛮之地,面向对象设计理念连半步都不敢踏入。“工具类是功能集中,而且逻辑绝不重复(Do not repeat yourself)” 一些开发人员会这样喊道 ,一般就是他们编写了这些工具类。由于全部都是静态的,因此它很快 - 团队里面的另一些人这样说,也许就是是添加另一些静态方法的人。它很容易使用,咱们使这些代码很简洁 — 你能够在这个空间内听到这样的言论,但这又是另一个对KISS的误解了。并发


咱们会争论到:一般帮助类和工具类都很简单,特别是当咱们不能修改新功能的目标类(例如外部依赖库)或咱们不能找到使用的目标(不清晰的领域模型,PoC,需求缺失),或者咱们只是不想去找它(懒,这也是帮助类的最主要缘由)。可是最大的问题在于这很明显不是面向对象的解决方案,而且随着时间的推移(缺乏团队沟通,资源重用,快速修复和一些其余的东西)它会致使一些包含无尽静态方法的容器和使人头疼的维护(你想要作到DRY,但你倒是用10个方法来提供几乎相同的功能,尽管不是彻底同样;你想要快速,但你如今不能方便地添加一个cache机制到那个静态类中或者你遇到了并发的麻烦;你想使事情变得简单,但如今你的IDE提供了一长列的各类各样的方法,这并不能简化你的工做)。但不要担忧,咱们会尝试着去解决它。app


让咱们来重构帮助类ide


首先,咱们须要定义咱们的问题:一个只提供静态方法的无状态类(有Helper或Utils后缀),它没有明确的职责,在项目中也不会被初始化为对象。函数


接着,咱们须要一个几乎明确的方案来解决问题。这几乎就表明了例外和项目特性:最后的决定固然是根据具体的状况来了,任何被称为通用解决方案的基本上均可以忽略。咱们最后须要分析一下给出的类,尝试着:工具


  • 找到一个肯定静态方法从属的目标类优化


  • 或找到这个类实际提供的目标业务实体,而后把它迁移到相关的组件,重命名而且删除静态方法(替换它们)ui


  • 或者经过面向对象方式添加一个提供一个或多个行为(以前存在的静态方法)新类。编码


上面的任何方案均可以提供一个更好的模型。而后咱们再依据下面的步骤(假设根据下面的步骤进行项目重构):spa


  1. 为了使咱们的任务简单些,咱们删掉项目的帮助类中没用的方法(你的IDE将会帮你大忙)。设计


  2. 接下来咱们把class定义为final。你看到项目中有编译错误了吗?若是有,为何帮助类或工具类须要被继承呢?你也许已经有一个目标:子类。若是子类是另一个帮助类(真的吗?),把它和父类合并吧。


  3. 若是不存在,咱们为该类添加一个私有构造函数。你看到项目中出现了编译错误了吗?那么确定在哪一个地方初始化了这个类,因此这并非单纯的帮助类或者它没有被正确使用。看一下那些调用方,你会发现一个或一系列方法均可能属于这个目标类(或者实体)。


  4. 让咱们经过必定规则相似的签名来分组类方法,将它们拆分到更小的帮助类中(从繁杂到有共性的方法,那个共性也许就是咱们须要的目标实体了)。一般到了这一步,咱们会从一个大的工具类向更轻量的帮助类过渡(提示:这时候不要惧怕建立一个只有一个方法的类),同时咱们的范围缩小了(从ProjectUtils到CarHelper,EngineHelper,WheelHelper等等)。(好,你的代码难道看起来不是更简洁了吗?)


  5. 若是这些新类只有一个方法,咱们须要看一下它的用途。若是咱们只有一个调用者,那么恭喜你,那就是咱们的目标类了!你能够把方法移到类中,做为behavior或私有方法(保持它的static标识或者利用内部状态)。这个帮助类就消失了。


  6. 咱们目前获得的帮助类(可是它确实能够成为你的起点)肯定了这些关联方法的一个通用状态。提示:看一下那些方法中的大部分通用参数(例如,全部方法都接收一个Car对象),这代表,这些方法可能应该做为方法属于Car类(或者扩展?封装类?)。不然,这些通用的参数应该是一个能够传给构造函数而且被全部(非静态和其余的)方法使用的类的属性,状态。那个属性应该会使你想起类的前缀,方法的归类可使你想起一系列行为的类(CarValidator,CarReader,CarConverter等等)。那么这个帮助类又能够去掉了。


  7. 若是这堆方法根据可选的输入和一些相同输入参数来使用不一样的参数,那么考虑经过使用建造者模式(Builder pattern)定义可变的接口来转换这个帮助类:从一系列相似Helper.calculate(x),calculate(x, y),calculate(x, z),calculate(y, z)的静态方法咱们能够简单地想到如newBuilder().with(x).with(y).calculate()。帮助类会提供behaviours,减小业务方法列表,而且提供更好的扩展性。调用方能够把它看成内部属性来重用或者在须要的时候再初始化。这个帮助类(咱们所知的)又能够去掉了。


  8. 若是帮助类提供的方法确实是供不一样的参数使用的(但,在这个时候,都是用于同一对象的),能够考虑使用命令模式(Command pattern):调用方实际上建立必须的命令(处理必须的输入和提供必要的操做),在肯定的上下文状况下会有一个调用者进行执行。你也许能够获取到每一个静态方法的命令实现,你的代码也从Helper.calculate(x,y),calculate(z)变成了invoker.calculate(new Action(x, y))。帮助类再见。


  9. 若是帮助类提供的方法接收相同的参数,但处理不一样的逻辑,能够考虑使用策略模式(Strategy pattern):每个静态方法均可以简单地变成一个策略实现,从而消除原来的帮助类(取而代之的是上下文组件)。


  10. 若是须要处理的多个静态方法涉及到一个类层次或一系列的组件,能够考虑使用访问者模式(Visitor pattern):你能够根据不一样的访问方法获得几个访问者实现,这也许能够替换部分或全部以前存在的静态方法。


  11. 若是以前的状况都不符合你的状况,那可使用三个最重要的指标:你的经验,你的项目能力和直觉。


总结


过程很简单,找到对的实体和合理的目标类或者经过一种采用面向对象设计的标准方法来重构给定的帮助类(但会在代码复杂度上有所增长,值得吗?)。过一下上面提到的场景列表,也许当你尝试理解怎么去实现重构时会有多于一个将会为你提供灵感;特定的限制也许会限制已肯定的解决方案;复杂的静态方法和相关的流程也许须要几个重构的步骤,能够一直优化它直到获得可接受的结果。或者你能够选择在某种程度上以代码可读性和简单性的名义来维持原来的帮助类(但愿能知足上面至少5个步骤)。帮助类并不都是有害的,但绝大多数状况下你并不须要它们。


参考: 如休整脱离帮助类和工具类参考自咱们的JCG成员 Antonio Di Matteo重构的建议 。

相关文章
相关标签/搜索