工作一年有余,最近负责项目组内代码重构。简单说下我的体会。
很多时候,我们都在说面向对象编程,可面向对象到底怎么理解,估计每个人的想法都不一样。很多人会告诉你一些很理论的东西。
比如“封装”“继承”“多态”“单一职责”“依赖倒置”等等一系列高大上的名词。更有甚者会搬出设计模式之类更加高大上的东东。
好像不掌握这些,就写不出“优雅”的代码。
不管别人怎么想,我觉得,所谓的面向对象方法,并不能严格算作编程方法,或许叫它代码管理方法更合适一点。
它可以算是一种标准,一种组织代码的方式。抛却过度设计的情形,你的代码,应该呈现出某种结构,而这种结构,应当使半个月后进行维护修改的人看得懂。
我不太赞成一直埋头写代码而不去考虑代码风格,我所在的公司,存在很强的加班文化,也确实,任务一直都挺赶。但每次加班的时候,我都不会为了赶进度去码代码,而是停下来,想想代码里有哪些可以优化的结构。从某种意义上说,我不能算是个好员工。
可能某天来个任务,让你实现一块黑板,让人可以在上面用白色笔进行画写擦除,你分析下,上网找找相关API,在半小时内完成了,然后去赶其他任务,比如登录注册之类。
两天后,需求改动,让这块黑板上可以用“红黄蓝”三种颜色的笔进行书写,ok,API稍稍整合下,接着以前的代码,继续写。
再过两天,需求又变动,不仅要可以擦除笔迹,还要可以选择某块区域选择性的擦除,ok,再看看API,任务也很快完成了。
再来两天,产品设计人员又说啦,这画笔的笔迹要有区分,不然怎么突出强调呢?于是乎,又加了一块设定笔迹粗细的代码。
再过两天,产品又说啦,这演示出来的效果不够炫,不能吸引人,我要再实现一种不同于现在的画笔的笔,这种笔画出来的笔迹在3秒后会闪一闪然后消失,恩,就叫荧光笔吧。
于是,又加了一段。基本上代码不整理下是不太能看了。没关系,还能忍。
第二天,产品出差回来:竞争对手也设计了这个模块,他们有个新功能,叫调色板!我们必须有!否则怎么和他们竞争!于是乎,你觉得代码有点理不出头绪了。想整理下,卧槽,只给了半天时间开发,明天就要演示,没时间折腾,草草糊弄个能用的模块,确保演示不出大问题,先拿下订单再说!
ok,也许这些都还能hold住。半个月后,公司结交个战略伙伴,他们有个很厉害的识别引擎,于是乎,你的黑板模块,需要集成这个牛哄哄的东西。这需要在书写笔迹的时候,收集一些点迹坐标,你还能找到在哪添加代码吗?ok,你是个大神级人物,记忆力还不错,照样在一天之内搞定了。
稍等,测试又来给你提bug了:这画下一横之后,用板擦擦除中间一部分,笔迹上,显示应该是两个“一”,这识别成一个一,不合适吧。
于是乎,在记录点迹的时候,还要考虑讨厌的板擦。
功能还没提交,测试又说了,我现在黑板右边写个一,再往左边写个二,看起来,笔迹是“二一”,你这识别成“一二”,不太合适吧。
……
再再然后,开发这块的人找到更好的去处,辞职了。
……
这差不多是个真实的故事。等到真正有时间重构这一块代码的时候,可能已经没有人能够看得懂了。架构师告诉我,你简单看下,能用就用,不能用就重写吧。
试试看吧,还好,蛮独立的一个模块,2000多行,从上到下读一遍,充斥了不少重复函数和乱七八糟的异步交互,还有耕种各样的不知何意的命名变量。
放眼望去,“鼠标按下”“鼠标移动”“鼠标抬起”三个大交互函数,然后里面各种各样的if...else...
简单梳理下,拆分了下代码,弄出来一堆文件,每个文件里一个类:
画笔类,荧光笔类,板擦类,调色板类(包括改变笔迹粗细的那部分也在这里),手势类,引擎识别接口类。
其中,引擎识别接口类只访问手势类,手势类由画笔板擦生成修改,画笔和荧光笔类访问调色板类。
最后,全部整合成黑板类,对其他模块来说,就好像没有变一样。
最初没有打算弄出手势这么个类,但后面发现一个数组操作起来确实不怎么方便。
再再后来,为了简化,干脆弄出两块画布,一块用来让笔画,另一块用来让黑板擦。这样判断鼠标状态就省事多了。
理论上蛮简单,想把这一切全部抽出来相互独立,还是花了一天时间。
就在我纠结要不要把两块画布按以前的样子合并成一块的时候,很快,产品又觉得一点点擦除的设计太蠢,要用鼠标拖出一块矩形区域,这个区域内所有的笔迹都要清除。
瞬间就觉得自己很机智的样子。因为拖拽出矩形是一个交互过程,画布上的矩形是要不停的擦除——获取当前鼠标位置——绘制更大矩形的过程,而这一过程中,区域内的笔迹是不可以被擦除的。直到鼠标抬起时,再将下面画笔的画布上的笔迹清空。
短的代码自然有短代码的好处,至少,能够很快的定位,无论是改bug还是加功能,都比在长代码里面查找方便的多。
以前我对代码风格也不怎么在意。只是看到项目里面随意的命名不爽。也许,要怪自己总是望文生义吧。
这个模块里paint指代笔迹,stroke指代手势,到下个文件可能就变味了。
有些是为了赶任务遗留下的问题,比如,今天,产品脑袋一拍,我们做个资源搜索的模块出来,展示我们的资源,这功能,就叫资源库吧!
哼哧哼哧弄出来了,产品又变想法了,恩,这个叫“资源库”可能不准确,改成“搜索面板”好了,我来重新设计下“资源库”的功能。
于是,以前的代码里到处是“reslib”的词眼,想改动,不好动。于是,这块干脆不动了,弄个新模块,到处是“ziyuanLib”的字眼(更糟糕的可能叫“ResLib”)。
可能,两个月后,另外一个人被要求维护现在的“新”资源库,然后找半天“reslib”找不到入口代码在哪。
亦或者,“资源库”和“搜索面板”同时设计,同时完工,快发布上线的时候,出于对产品的考虑,产品决定把已有的“资源库”对外称作“搜索面板”,而“搜索面板”对外称作“资源库”。
经过,两个版本,人员交替,抽调的抽调,离职的离职,跳槽的跳槽。
再来个人维护,你可以想象下维护者满脸零乱的表情。
再后来,和其他组合作的时候,我都要求对方把接口里每一个固定的字面参数代表什么含义写成文档,签字画押。。。。
不要以为代码里出现“ZhangSan”就真的指代“张三”,说不定原本“张三”叫“李四”,然后哪天产品心血来潮,把“张三”改个名。
年初参加过一个百度的开源项目,Ueditor。虽然没啥贡献。
不过,看了里面的源码,才明白统一风格对于团队有多重要。当时看的时候,感觉核心部分还好,蛮一致的。
后来要添加功能,需要我为其加插件了,翻翻其已有的插件,不能说复用性很低,至少可读性很低。
举例来说,核心那块代码,用Editor指代编辑器本身,用me指代当前模块。
然后有些插件里面,就用me指代编辑器,然后看代码的时候,总造成障碍。
后来,项目做到一半被领导层放弃了,就再没看过Ueditor。不知道现在怎么样了。
从那以后,我对开源的大型项目持保留意见。
现在,我所在的项目里,比较讨厌的东西是自定义事件,我觉得,应该存在某种方法把这种太过灵活的编程方式废弃。
用得好,是亮点;用烂了,就成负担了。
不知道什么时候什么东西就在哪里加上一个事件,也不知道什么时候就触发了这个事件。整个代码乱七八糟的。
最后一点点不太理解的东西,就是面向对象里面的“私有”“公有”“保护”的区别。
直到现在,都不太理解。在程序员手上,“保护”类型连自己都“保护”不了,还提什么“保护”?
只要程序员愿意,任何时刻都能删除或改成“公有”。