博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
明明开发时间很赶,我为什么还要重构整个项目
阅读量:4215 次
发布时间:2019-05-26

本文共 4347 字,大约阅读时间需要 14 分钟。

最近在一次App版本接口迭代时,把一个维护了两年的旧项目接口进行了重构,下面简单用这一篇文章交代一下重构的过程:

为什么要进行重构

首先谈为什么要进行重构吧,毕竟已经维护了两年了,大大小小也经历了很多次迭代开发,为何这次会进行重构呢?

这要先交代一下我们这个接口项目是咋回事。

一般一个App项目如果业务比较复杂的话,可能会将接口划分到多个项目中,当然,我们的App也是这样,而我重构的这个项目,也是比较新的一个模块的接口,由于刚开始比较简单,所以没有选择现在成熟的框架,而是用PHP自己了写了一个简单的MVC框架,其实项目的目录结构大体如下:

controlers #控制器层models #模型类层common #公用函数config #配置文件目录libs #基础类库ext #第三扩展index.php #入口文件

复制代码从代码的项目结构看得出来,最主要的是控制器层和模型层:

控制器层(controllers)

负责接收请求参数,并且也堆积很多业务逻辑,同时调用模型类完成业务逻辑,返回数据,在controllers目录下,由于要为不同的App版本提供接口,所以子目录格式为v1.x.x,以些来区别不同版本的接口。

controllers    v1.0.0    v1.0.1    ...    v5.0.0

复制代码模型层(models):

业务逻辑层,也包含对数据进行CURD,调用第三方,参数校验等功能,几乎所有业务功能都在这里,不同版本的模型层相互调用,所以这一层也是功能最混乱的层,因此也是最需要重构的分层,models目录的版本划分与controllers类似,不同版本的model类有大量重复的代码。

models    v1.0.0    v1.0.1    ...    v5.0.0

复制代码通过上面的介绍,其实你会发现,这个是一个简单到不能再简单的项目,但再简单的项目,随着业务的推进以及多个迭代开发,代码的代码极其混乱,总结起来大概是以下几个问题:

没有良好的目录分层,大体上只是简单分了控制器层和模型层,所有的业务逻辑都堆积在控制器层和模型层,完全没有任何扩展性可言。

很多配置都直接写死在代码里,虽然项目中有专门存放配置的目录,但还是有很多的配置直接写死在代码,比如连接redis的代码。

没有编码规范,无论变量名还是常量或是类名,命名都很随意。

代码里还充斥着大量的魔法数值,如何有新人接手,会完全搞不清楚到底这些数值到底什么意思。

所有的代码都没有输出日志,出现BUG时,很难定位问题。

业务代码直接堆积在控制层和模型层,没有抽离公共代码,新增一个版本接口时,需要完全复制上个版本的代码。

完全没有使用PHP的命名空间,不能很好划分不同的类。

简而言之,这是一个项目结构极其简单,但由于经过太多人维护而代码变得极其混乱且没有规范的应用,虽然我接手后一直有重构的项目,奈何之前只是个别接口的迭代,而在最近一次App迭代时,几乎所有接口都是新的版本,因此重构是不可避免的,哪怕时间不够,为了之后的版本。

重构过程

上面列出项目的几个问题,其实重构的过程,就是把上面提出问题的优化吧。

重构要达到的目的

重新划分项目结构,使项目结构层级更加清晰。

进行版本迭代开发时,不需要复制一份同样的代码,提升开发效率。

增加代码的可维护性。

重构的原则

单一职责原则:每个类的功能要单一,每个方法的作用要单一,避免代码臃肿。

变量命名规范

避免在代码中直接写魔法数值

迭代接口,新增一个接口版本时,不需要完全复制上一个版本接口的代码。

重新划分的项目结构

controllers # 控制器层,只能将参数传递给services层。    v1_0_0        IndexController.php    v1_0_1    ......    v5_0_0services #具体业务逻辑层,可以调用manager层或models来实现业务逻辑    v1_0_0    v1_0_1    ......    v5_0_0handlers # 扩展层,对services的补充models # 模型,针对数据表的CURD代码entity #实体类managers #业务封装层 libs #类库ext #第三方类库common #公用函数index.php #入口文件

复制代码下面的项目的目录结构的截图:

在这里插入图片描述
一个controller类的示例:

namespace app\controllers\v1_0_0;use app\services\v1_0_0\IndexService;class IndexController {    private $service = null;    public function __construct(){        $this->service = new IndexService();    }        public function actionIndex(){        //接收参数        ....        调用IndexService的业务方法,并返回值给客户端        return $this->service->index();    }}

一个service类型的示例:

#不同版本的service之间通用代码抽取到Service类,作为基类被继承namespace app\services;class IndexService{        public function __construct(){            }        //业务方法    public function index($page = 1){        //具体业务逻辑        $list = $this->getArticle();        return [            'list' => $list,            'total' => 100        ];    }        protected function getArticle(){            }}

在重构过程中,之所以设计如上所示的目录结构,一个重要的考虑点就是如果在新增加一个版本接口时,最大限度地复用上一个版本的逻辑,这里的新增接口的意思是现在首页接口的版本为v1.0.0,但由于版本迭代,会把接口升级为v1.0.1,也就是接口升级。

一个接口的升级,无非两个原因:

接口的业务处理逻辑发生改变,因此需要升级接口。

接口的数据结构发生改变,比如新增数据或数据类型发生改变,因此需要升级接口。

比如说,index接口从v1.0.0升级v1.0.1时,getArticle()方法数据结构或者业务逻辑发生改变,这时候继承父类接口,并覆盖getArticle()方法即可。

namespace app\services\v1_0_1;use app\services\IndexService IndexBaseService; class IndexService extends IndexBaseService{        public function __construct(){            }        //重写覆盖父类逻辑    pulic function getArticle(){            }}

但这时候,你会发现在getArticle()方法重写的逻辑,只在v1.0.1这个版本中,如果这个重写的逻辑在后续版本也是一样的,那不是每个版本都要重写?

这时候,可以将这段逻辑抽取出来,给每个需要的版本复用,如果某个有单独的处理逻辑,可以使用的的覆盖重写的方法,而抽取出来的逻辑,放在handlers目录结果中,handlers目录是对services中需要重写覆盖并会在多个版本复用逻辑的抽取层。
所以我们把getArticle()方法抽取出来,如下所示:

namesapce app\handlers;trait getArticle{        public function getArticle(){            }}

这时候v3.0.1的接口,加载上面handlers的方法,完全逻辑复用。

namespace app\services\v3_0_1;use app\services\IndexService IndexBaseService; use app\handlers\getArticle;class IndexService extends IndexBaseService{        use getArticle;        public function __construct(){            }}

重构的结果

老实说,项目开发时间不够,而我个人能力也有限,因此我只能在本次的开发中,尽自己的能力去完善整个项目架构,很多不完善的地方,只能之后的开发中优化了。

重构后的优点

相比原先全部业务堆积在models层,划分后的架构,每个层级只负责自己的事情,因此逻辑比较清晰,代码可维护性强,每个类或方法的职责单一,降低了开发难度。

重构后的缺点

当然,原来的代码非常简单,就是controllers层直接调用models层,或者有时候,所有的业务逻辑直接写在controller层,重构后,代码的复杂性也会相应增加。

为什么是重构而不是重新开发一个新项目呢?

可能很多人会问,这么简单的项目,直接重做不就好了吗?其实是这样,除了上面重构中的重新分层,项目还有很多公用的代码和模块,如果重新开发一个项目的话,那么这些基础的也需要重新开始,而旧项目也需要继续维护,更加增加开发和维护的成本,因此重构是比较合适的选择。

小结

老实说,我重构的这个接口项目一点也不复杂,最主要的代码还是CURD,并没有非常复杂的业务逻辑,但由于从一开始就没有进行严格的代码架构分层,在开始过程也没有一致的代码规范,导致经过两三年业务的发展与代码迭代开发,造成了代码混乱和重复业务逻辑堆积。

所以,对于任何项目来说,良好的代码分层和开发规范,是保证项目可维护性的根本。

最后

在这里插入图片描述

想获取面试资料 在评论下方回复面试资料哦!
在这里插入图片描述
作者:张君鸿
出处链接:https://juejin.im/post/5e021a04e51d45582b2a427b

转载地址:http://ydimi.baihongyu.com/

你可能感兴趣的文章
Python学习笔记——大数据之Spark简介与环境搭建
查看>>
Python学习笔记——大数据之SPARK核心
查看>>
Python学习笔记——大数据之Pyspark与notebook使用matplotlib
查看>>
Python学习笔记——云计算
查看>>
Python学习笔记——运维和Shell
查看>>
Python学习笔记——nginx
查看>>
Python学习笔记——自动化部署
查看>>
Python学习笔记——多任务-协程
查看>>
Python学习笔记——WSGI、mini-web框架
查看>>
Python学习笔记——闭包、装饰器
查看>>
Python学习笔记——mini-web框架 添加路由、MySQL功能
查看>>
Python学习笔记——mini-web框架 添加log日志、路由支持正则
查看>>
Python学习笔记——元类、实现ORM
查看>>
Python学习笔记——Celery
查看>>
Python学习笔记——数据分析之Matplotlib绘图
查看>>
Python学习笔记——数据分析之工作环境准备及数据分析建模理论基础
查看>>
Python学习笔记——数据分析之Seaborn绘图
查看>>
Python学习笔记——数据分析之Bokeh绘图
查看>>
Python学习笔记——数据分析之数据可视化工具实战案例:世界高峰数据可视化
查看>>
Python学习笔记——科学计算工具Numpy
查看>>