热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

重构,让人快乐让人苦

重构,是编写代码必须要面对的一项操作,同时也应该是程序员乐于实践的一项内容。不论是逻辑实现还是设计过程,乃至整个分层结构,我们都可能面临并且实施重构。这篇文章不会告诉您什么是重构,如何去优美的重构等等的理论,只想和大家分享一些感受,并且探讨一些问题。最近的两周,我一直对我们团队的一个子业务框架做重构的工作,很多地方让我

重构,是编写代码必须要面对的一项操作,同时也应该是程序员乐于实践的一项内容。不论是逻辑实现还是设计过程,乃至整个分层结构,我们都可能面临并且实施重构。这篇文章不会告诉您什么是重构,如何去优美的重构等等的理论,只想和大家分享一些感受,并且探讨一些问题。最近的两周,我一直对我们团队的一个子业务框架做重构的工作,很多地方让我感到很痛苦,于是便有了这篇文章。

牵一发而动全身的根源在哪里

当我打开解决方案,查看代码的时候,我们会发现很多问题,比如冗余的代码,性能低下的逻辑实现等等,但是当我着手去改造的时候,潜意识告诉我整个似乎不能动,牵扯的面太广了。更改一个小地方,上下一串都要做相应的调整,这当然不是我想要看到的。大范围的调整会直接影响系统的稳定性,带来潜在的危险,同时会增加测试团队的负担;在版本控制方面会造成线上和线下版本在同一内容的巨大差异,版本更新的时候拿什么来保证一套几乎全新的代码替换线上系统是正确的选择呢?因为很多问题只有在最真实的环境才能被暴露出来。

这样的修改,修改成本无疑是巨大的,因为我们期望修改的只是那一小块代码而已。大范围的代码调整,同时也伴随着单元测试代码的调整。测试团队如果因此来重新走测试用例,那么付出的辛苦可想而知。

我要做的是重构而不是重写,造成这种现象的原因在哪里呢?

整个解决方案具有相对完整的分层结构,DAO层、实体层、业务逻辑层。实体层也对数据实体和业务实体做了分别定义。但是进行业务实现的时候我们并没有进行有效的隔离和代码的职责划分。

很多代码在处理业务逻辑的时候直接调用DAO,然后使用返回的数据组织业务实体。当我们的业务实体需要按照领域划分为两个或者更多的层次的时候,结果会变得更为糟糕,因为我们需要以底层的业务实体为输入从而输出上层的实体。当你以一个顺序工作流的方式完成一整套操作的时候,也许感觉很有成就感,整个过程天衣无缝,完美无缺。但是当我们尝试改变其中的某些内容的话,噩梦就开始了,实体的改变势必会引起逻辑的改变,但是这种改变是有连锁反应的。

业务实现的过程很多时候就是不同层次间的实体的转化过程,那么实现过程中单单考虑解除依赖不能收到很好的效果,从业务逻辑的职责出发,划分出清晰的业务层次,再以实体转变的结合点来考虑分解才能达到良好的效果。

独立的领域层尤为重要

各种经典的MVC架构的实现,常常让人产生误解,认为那样做就已经完美了。事实上,一个业务的框架的重点不是增、删、改、查,我更倾向于将DAO从业务框架中分离出去(最后我也是这样做的),整个系统应该提供统一的DAO服务,子业务框架要专注于业务的实现。

当我们尝试将一整套业务实体独立出来的时候,我们认为已经做了很好的业务理解,但是这是只见树木不见森林的想法。在某些系统中,领域的实现只占代码总量的很少一个比例,但是其重要性往往却是相反的一个比例。当我们选择将领域代码和其他代码混合在一起的时候,意味着我们的分层结构也随之混乱。

"用标准的架构模式来完成与上层的松散关联。将所有与领域相关的代码都集中在一层,并且将它与用户界面层、应用层和基础结构层的代码分离。领域对象可以将重点放在表达领域模型上,不需要关系它们自己的显示、存储和管理应用任务等。这样使模型发展得足够丰富和清晰"。在清楚了整个领域模型之后,再考虑选择合适的模式来解决分层问题,我觉得是合理的做法。

在对业务和领域没有充分理解的时候不要下手

在重构过程中,发现很多业务实体的定义不着边际,很多概念只是对数据实体的拓展,结果出来的东西和数据实体的逻辑关系截然不同。对数据调取的逻辑没有充分理解,那么组织业务实体的时候很容易出现不恰当的数据访问方式,比如循环访问数据库。

如果整个领域模型的建立和划分都是错误的话,我们仍然能实现所需要的功能,但是如果对这样的代码进行重构无疑等于推倒重来。

现在看来,当我们为了实现功能而急匆匆的不择手段的时候,为将来的维护和升级埋下了隐患。当我们想要将公用的数据调取和业务逻辑实现从各个子项目中抽象出来变成基类、接口、Helper或者Service的时候,我发现不同子项目的开发者对业务和领域的理解有着很大的差异,因而在实现方式和实体定义上都有很大的不同。这个时候我们又注意到组织实体的逻辑并没有单独的分离出来,抽取的工作遇到了难题。也许我们可以分离出代理,然后使用Adapter来适应原有的实体组织逻辑,又或许我们应该废除不合理的实体定义,也意味着废除了相应的实现逻辑。

如果我选择将不合理的实体替换为正确的实体定义将面临巨大的挑战,也许从数据调取到最终的逻辑都要调整。当然全面调整的原因是我们没有实现很好的隔离。

迷茫,面对一个几千行的Method

这是我无法容忍的情况,整个子业务的实现几个大方法全搞定了,每个方法里无数个"Region"和"End Region"。这样的情况就别谈什么分离和设计了。最起码的,当你在用"Region"和"End Region"的时候,就应该意识到这里可以分离出一个方法来。

大方法带来很多弊端。它的可维护性差,可阅读性差,和系统的分层结构不融合,不能进行有效的单元测试。当然对大方法的重构并不像代码本身那么发杂,如果它的逻辑足够清晰的话。但是如果一个思维足够清晰的程序员又怎么会写出这样的代码,所以对这样的代码进行重构,面临一个很大的问题就是那些在不同逻辑里重用的局部变量。当然更重要的问题是理不清头绪。

光去抱怨是没什么意义的,这样的方法出现的原因是什么呢?一个是开发者没有很好的理解业务框架的结构和目的,二是对程序设计的基本思想理解的不够好,三是对业务逻辑本身理解的不够清晰。对于这样的实现去重构,除了从业务角度出发,抽丝剥茧,慢慢的剥离,还有什么好办法呢?推倒重来吗?

重构,要随时进行

当有一份代码觉得不合适,而没有及时重构的话,那么整个解决方案就可能变成垃圾场。后续的开发人员会以存在即合理的想法来看待这些垃圾代码。尤其是新加入的成员,只能模仿别人在怎么做。从测试驱动的开发理念看,程序开发是一个不断重构的迭代过程。很多人将重构看成是一件大事,一听到重构就害怕起来,尤其是测试团队。当然这里不能否认,不恰当的重构会给测试团队造成很大的麻烦。

集中重构是极其错误的思想。不要想着等某些开发任务结束了,有时间了再集中精力来重构代码。当系统相对稳定之后,重构要付出的代价可能是整个团队无法接受的。对分层架构的重构应该是建立架构的最初一段时间,不断的调整达到最优。当项目进行一段之后,再来调整整体结构无疑是让人无法接受的。

重构要避免过度设计

最后要说的是,重构要围绕一个适度的目标来进行,要考虑代价,同时不代表模式应用的越多越好。相反的,在重构过程中,要时时考虑是否把简答的事情想复杂了。

目前我重构的代码中,还没有这样的问题,这里也就不啰嗦了。

说了这么多,我还是想听听各位的看法和感受,如何进行有效的重构,如何在编程的最开始的阶段就避免很多重构障碍的产生?

本文地址:http://www.nowamagic.net/librarys/veda/detail/979,欢迎访问原出处。


推荐阅读
  • SpringMVC RestTemplate的几种请求调用(转)
    SpringMVCRestTemplate的几种请求调用(转),Go语言社区,Golang程序员人脉社 ... [详细]
  • 前言无论是对于刚入行工作还是已经工作几年的java开发者来说,面试求职始终是你需要直面的一件事情。首先梳理自己的知识体系,针对性准备,会有事半功倍的效果。我们往往会把重点放在技术上 ... [详细]
  • 本文探讨如何利用Java反射技术来模拟Webwork框架中的URL解析过程。通过这一实践,读者可以更好地理解Webwork及其后续版本Struts2的工作原理,尤其是它们在MVC架构下的角色。 ... [详细]
  • springMVC JRS303验证 ... [详细]
  • 本文详细记录了一位具有五年半开发经验的候选人,在华为Android高级开发职位面试过程中的经历。从早晨9点到下午5点半,经过了群体面试、技术面试、综合面试及英语面试等多个环节,最终成功通过考核。文章不仅分享了面试心得,还提供了宝贵的面试题资源。 ... [详细]
  • 探讨了在 Spring MVC 框架下,JSP 页面使用 标签时遇到的数据无法正确显示的问题,并提供了可能的原因和解决方案。 ... [详细]
  • 深入解析Spring Boot自动配置机制
    本文旨在深入探讨Spring Boot的自动配置机制,特别是如何利用配置文件进行有效的设置。通过实例分析,如Http编码自动配置,我们将揭示配置项的具体作用及其背后的实现逻辑。 ... [详细]
  • 本文探讨了2019年前端技术的发展趋势,包括工具化、配置化和泛前端化等方面,并提供了详细的学习路线和职业规划建议。 ... [详细]
  • Asp.net MVC 中 Bundle 配置详解:合并与压缩 JS 和 CSS 文件
    本文深入探讨了 Asp.net MVC 中如何利用 Bundle 功能来合并和压缩 JavaScript 和 CSS 文件,提供了详细的配置步骤和示例代码,适合开发人员参考学习。 ... [详细]
  • MySQL锁机制详解
    本文深入探讨了MySQL中的锁机制,包括表级锁、行级锁以及元数据锁,通过实例详细解释了各种锁的工作原理及其应用场景。同时,文章还介绍了如何通过锁来优化数据库性能,避免常见的并发问题。 ... [详细]
  • 本文深入探讨了JavaScript中实现继承的四种常见方法,包括原型链继承、构造函数继承、组合继承和寄生组合继承。对于正在学习或从事Web前端开发的技术人员来说,理解这些继承模式对于提高代码质量和维护性至关重要。 ... [详细]
  • 本文将详细介绍如何在ThinkPHP6框架中实现多数据库的部署,包括读写分离的策略,以及如何通过负载均衡和MySQL同步技术优化数据库性能。 ... [详细]
  • 掌握Spring MVC中自定义类型转换与格式化的技巧
    近期,在开发一款小程序的过程中遇到了几个Spring MVC接口需要传递时间参数的问题。本文将详细介绍如何利用Java 8 Time API在Spring MVC中实现时间参数的自定义类型转换和格式化。 ... [详细]
  • 本文探讨了浏览器的同源策略限制及其对 AJAX 请求的影响,并详细介绍了如何在 Spring Boot 应用中优雅地处理跨域请求,特别是当请求包含自定义 Headers 时的解决方案。 ... [详细]
  • 深入分析十大PHP开发框架
    随着PHP技术的发展,各类开发框架层出不穷,成为了开发者们热议的话题。本文将详细介绍并对比十款主流的PHP开发框架,旨在帮助开发者根据自身需求选择最合适的工具。 ... [详细]
author-avatar
416703721
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有