近来在维护并重构一个原来离职的同事的代码,在将一部分代码从原有逻辑剥离开来的时候遇到了很多的麻烦,希望自己以后不要留下类似的坑,所以在这里做一个总结
代码是Python写的,工程很浩大,其中代码运行在一个虚拟化集群中,我的目的是将一部分原有代码逻辑差不多,但是实体(机器)要分开的代码重构
其中涉及到主要的有几点约束:
1.新分离的代码逻辑和原有的代码逻辑要一样
2.其中涉及到一个任务系统,集群中的机器取任务和返回任务都是向一台核心Server获取,新分离的代码中任务申请和返回接口发生改变
3.暂时没想到...
从原有代码中分离出新代码,且代码逻辑要一样,那么很多人都会偷懒直接Copy一份代码,然后把其中一些部分稍微做修改
用脚趾头想想都知道这样以后的维护工作有多麻烦:
1.修改代码逻辑需要修改两份,原有的发生改变后你的也必须改
2.谁知道你和原有代码有哪些地方不一样,不要指望文档
最好的办法就是继承,继承原有代码的基础上重载你要修改的东西。
可是以前那位兄弟从来没考虑过他的代码是会被继承的,也就是说,他用了很多不适合被重载的方法在里面,例如直接暴露第三方库,全局变量等等...
这里就说道了这次开发回忆录的主题 第三方模块的松耦合
在这次的工程中,第三方模块依赖不多,主要是JsonRPC暴露的几个接口
原有的代码中大多数都是在类中直接import JsonRPC的Client然后引用一个配置文件对Client 做初始化
这样做的坏处显而易见,代码与第三方模块的紧耦合,在我的需求中,代码中申请任务和返回任务等一系列接口都是通过JsonRPC来做的,一旦我需要改变这些接口的功能或者指向的时候
在耦合很严重的情况下我不得不重写代码,那样在逻辑上代码就可能发生改变了,维护工作也很麻烦
在这样的设计中最好的方式就是遵循设计的一个很重要的原则,动静分离
说到动静分离,这里的动和静就分别指的是:
1.静:代码逻辑是不会变的,即使变那修改本身的需求,也就是平常写程序的那些if else的逻辑什么的...
2.动:
a.不属于你的类的函数调用(姑且看做是第三方模块),当然要排除一些常用的函数比如说字符串处理云云..
也就是说依赖别人的都是不可靠的,没有人会真正和你在一起约定一辈子,那么这部分东西就有可能是变化的
b.配置依赖,当我需要使用原有类的一些函数功能的时候,但是发现其中的配置和功能紧紧的耦合在一块儿,比如说初始化JsonRPC的客户端中服务器的地址
我在新的逻辑中需要改变这个服务器地址,没办法,只有重载初始化JsonRPC Client的函数,但是又发现一个问题就是初始化函数发生在构造函数中,于是子类
在调用父类的构造函数的时候JsonRPC就初始化,轮不到子类来初始化,所以这里JsonRPC的客户端都指向了原有的服务端地址
换句话说,这里的配置也是不可靠的,随时都可能会变化
做到动静分离很简单,避免直接调用,增加一个接口层(如果你是一个强类型的语言的OOP开发者),或者你就封装一个调用的类
第三方模块的调用都抽象为统一的几个操作并封装为另一个类,比如说我这里的:
1.申请任务操作
2.返回任务结果
动的代码依赖->我只需要在原有的代码逻辑中将这个类作为参数,以后在调用的时候用这个类直接调用(Python的鸭子类型)(其他的语言开发者就是利用多态的特性或者接口云云..来调用实际的第三方接口)
动的配置依赖->同样的将配置封装为统一的一个结构,初始化或者其他时候再动态绑定这个结构中的配置信息而不是直接引用配置文件中的参数
再补充一点:
工程中最好不要出现一个独立的没有归属的函数,即使有,这个函数也应该是可以配置的,大学课程时候一个讲程序设计艺术的老师说过,没有返回值的函数应该叫做过程。
这里我觉得一个功能函数不可配置(除非是真的不需要配置)不然都应该有一个归属