
背景1:项目的代码往往牵一发而动全身,业务逻辑耦合严重。
对于大的架构重构,其实一直很谨慎的。原则是将重构融合在每次迭代中,逐步优化代码的结构。然后将这个工作持续进行下去!
背景2:当初设计的架构让项目的依赖关系越来越复杂,维护成本也越来越高。
决定梳理并优化一下整个项目结构。在实施过程中,依然坚持将整个重构的过程融合在每个迭代中,逐步完成一次大的架构升级。
要求1:重构代码对一个工程师能力的要求,要比单纯写代码高得多
重构需要你能洞察出代码存在的坏味道或者设计上的不足,并且能合理、熟练地利用设计思想、原则、模式、编程规范等理论知识解决这些问题。
要求2:提高代码的质量
具体点说就是,提高代码的可读性、可扩展性、可维护性等。
要求3:多问自己为什么这样设计
在做代码设计的时候,一定要先问下自己,为什么要这样设计,这样做是否能真正地提高代码质量,能提高代码质量的哪些方面。
如果自己很难讲清楚,或者给出的理由都比较牵强,没有压倒性的优势,那基本上就可以断定这是一种过度设计,是为了设计而设计。
问题1:项目痛点在哪里。先要去分析代码存在的痛点,比如可读性不好、可扩展性不好等等,然后再针对性地利用设计模式去改善。
问题2:对重构理解不深入。对为什么要重构、到底重构什么、什么时候重构、又该如何重构等相关问题理解不深,对重构没有系统性、全局性的认识。
问题3:对重构没有技巧。面对一堆烂代码,没有重构技巧的指导,只能想到哪改到哪,并不能全面地改善代码质量。
软件设计大师 Martin Fowler 是这样定义重构:重构是一种对软件内部结构的改善,目的是在不改变软件的可见行为的情况下,使其更易理解,修改成本更低。
重构的定义很重要,有一个值得强调的点:“重构不改变外部的可见行为”。把重构理解为,在保持功能不变的前提下,利用设计思想、原则、模式、编程规范等理论来优化代码,修改设计上的不足,提高代码质量。
遇到问题再重构,维护代码的过程中,真正遇到问题的时候,再对代码进行重构,能有效避免前期投入太多时间做过度的设计,做到有的放矢。
重构围绕一个老生常谈的概念「解耦」「拓展」「维护」等维度展开,设定几个目标:
根据重构的规模:可以笼统地分为大规模高层次重构(以下简称为“大型重构”)和小规模低层次的重构(以下简称为“小型重构”)。
1.大型重构指的是对顶层代码设计的重构
包括:系统、模块、代码结构、类与类之间的关系等的重构,重构的手段有:分层、模块化、解耦、抽象可复用组件等等。
这类重构的工具就是使用学习过的那些设计思想、原则和模式。这类重构涉及的代码改动会比较多,影响面会比较大,所以难度也较大,耗时会比较长,引入 bug 的风险也会相对比较大。
2.小型重构指的是对代码细节的重构
主要是针对类、函数、变量等代码级别的重构,比如规范命名、规范注释、消除超大类或函数、提取重复代码等等。
小型重构更多的是利用我们能后面要讲到的编码规范。这类重构要修改的地方比较集中,比较简单,可操作性较强,耗时会比较短,引入 bug 的风险相对来说也会比较小。你只需要熟练掌握各种编码规范,就可以做到得心应手。
需要说明的问题
找到代码中的问题,可以看看项目中有哪些写得不够好的、可以优化的代码,主动去重构一下。或者,在修改、添加某个功能代码的时候,你也可以顺手把不符合编码规范、不好的设计重构一下。
思考1:进行大型重构的时候
要提前做好完善的重构计划,有条不紊地分阶段来进行。每个阶段完成一小部分代码的重构,然后提交、测试、运行,发现没有问题之后,再继续进行下一阶段的重构,保证代码仓库中的代码一直处于可运行、逻辑正确的状态。
每个阶段,我们都要控制好重构影响到的代码范围,考虑好如何兼容老的代码逻辑,必要的时候还需要写一些兼容过渡代码。
思考2:小规模低层次的重构
因为影响范围小,改动耗时短,所以,随时都可以去做。按照分,拆的思想不断优化代码。
思考3:借助工具分析代码问题重构
除了人工去发现低层次的质量问题,还可以借助很多成熟的静态代码分析工具(比如FindBugs、PMD),来自动发现代码中的问题,然后针对性地进行重构优化。
如何保证重构不出错呢?你需要熟练掌握各种设计原则、思想、模式,还需要对所重构的业务和代码有足够的了解。
除了这些个人能力因素之外,最可落地执行、最有效的保证重构不出错的手段应该就是单元测试(Unit Testing)了。
当重构完成之后,如果新的代码仍然能通过单元测试,那就说明代码原有逻辑的正确性未被破坏,原有的外部可见行为未变。
举一个简单的例子看重构事项
现状:App业务线各种依赖庞大,然后交错在一起,关系变的复杂。依赖库之间的强依赖导致版本冲突多。模块方案发生变化,上层修改成本大。功能模块兼容性导致维护成本大。
重构方案:整个架构的核心思想是面向接口编程和依赖注入使各个模块之间实现解耦,然后通过横向角色划分与纵向层级划分的方式约定各个模块之间的关系,再通过接口分层的方式,明确具体模块在不同层级上需要实现的功能
针对重构的业务详细罗列
针对一个比较大的重构业务,先进行梳理,然后根据问题或痛点思考,罗列解决方案,然后开始实践并保证代码稳定性,最后测试并交付。
罗列重构事项
比如针对该例子关键点
编写详细的测试用例
要把持续重构作为开发的一部分来执行
mock业务数据很重要
尤其是有接口请求的业务逻辑,这个时候可以mock本地json数据,然后模拟各种业务场景。十分有利于调试各种case
积极发现代码bug很重要。在代码演进中,bug避免重复出现很重要。作为实践写代码,这个过程自测case罗列一下,然后自测,自测一项勾选一项。
了解敌人——丑陋的代码
优化编码的具体操作:
为何要这样重构
如果自己很难讲清楚,或者给出的理由都比较牵强,没有压倒性的优势,那基本上就可以断定这是一种过度设计,是为了设计而设计。
不假思索地套用学过的设计模式
看到某个场景之后,觉得跟之前在某本书中看到的某个设计模式的应用场景很相似,就套用上去,也不考虑到底合不合适。
最后如果有人问起了,就再找几个不痛不痒、很不具体的伪需求来搪塞,比如提高了代码的扩展性、满足了开闭原则等等。
先遇到问题然后再改造,从问题讲起,一步一步给你展示为什么要用某个设计模式,而不是一开始就告诉你最终的设计。
看到某段代码之后能分析,就能够自己分析得头头是道,说出它好的地方、不好的地方,为什么好、为什么不好,不好的如何改善,可以应用哪种设计模式,应用了之后有哪些副作用要控制等等。
设计模式要干的事情就是解耦
也就是利用更好的代码结构将一大坨代码拆分成职责更单一的小类,让其满足高内聚低耦合等特性。
创建型模式是将创建和使用代码解耦,结构型模式是将不同的功能代码解耦,行为型模式是将不同的行为代码解耦。而解耦的主要目的是应对代码的复杂性。设计模式就是为了解决复杂代码问题而产生的。
根据是否复杂投入时间
架构设计要以实用为目的:不要光想着造一个世上最牛逼的架构,这样往往是不靠谱的,我们不是救世主。一切都是当前实际情况为主!
总结下,架构设计有三个基本原则:
这三个原则也是有优先级的。具体是:合适优于先进 > 演化优于一步到位 > 简单优于复杂
合适也就是适应当前需要是首位的,连当前需求都满足不了谈不到其他。架构整体发展是要不断演进的,在这个大前提下,尽量追求简单,但也有该复杂的时候,就要复杂,比如生物从单细胞一直演化到如今,复杂是避免不了的。
模块 | 描述 | 备注 |
|---|---|---|
GitHub | 多个YC系列开源项目,包含Android组件库,以及多个案例 | |
博客汇总 | 汇聚Java,Android,C/C++,网络协议,算法,编程总结等 | |
设计模式 | 六大设计原则,23种设计模式,设计模式案例,面向对象思想 | |
Java进阶 | 数据设计和原理,面向对象核心思想,IO,异常,线程和并发,JVM | |
网络协议 | 网络实际案例,网络原理和分层,Https,网络请求,故障排查 | |
计算机原理 | 计算机组成结构,框架,存储器,CPU设计,内存设计,指令编程原理,异常处理机制,IO操作和原理 | |
学习C编程 | C语言入门级别系统全面的学习教程,学习三到四个综合案例 | |
C++编程 | C++语言入门级别系统全面的教学教程,并发编程,核心原理 | |
算法实践 | 专栏,数组,链表,栈,队列,树,哈希,递归,查找,排序等 | |
Android | 基础入门,开源库解读,性能优化,Framework,方案设计 |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。