深入探讨跨端、IoT 动态开发、DevOps等大前端方向热门技术话题,这里直达 了解详情
写点什么

把同事的代码重写得干净又整洁,老板却让我做回滚?

  • 2020 年 2 月 06 日
  • 本文字数:2847 字

    阅读完需:约 9 分钟

把同事的代码重写得干净又整洁,老板却让我做回滚?

夜深了。


我的同事把这周写的代码提交了。我们在开发一个图形编辑器画布,已经实现了形状调整功能,即通过拖拽形状边缘的手柄来调整形状(比如矩形和椭圆形)。


代码可以运行。


但重复代码有点多。每一种形状(比如矩形和椭圆形)有不同的手柄,往不同方向拖拽手柄对形状的位置和大小影响也不一样。如果用户同时按住 Shift 键,在改变大小的同时要保持比例不变。这里涉及了很多数学运算。


代码看起来像这样:


let Rectangle = {  resizeTopLeft(position, size, preserveAspect, dx, dy) {    // 10行重复的数学运算代码  },  resizeTopRight(position, size, preserveAspect, dx, dy) {    // 10行重复的数学运算代码  },  resizeBottomLeft(position, size, preserveAspect, dx, dy) {    // 10行重复的数学运算代码  },  resizeBottomRight(position, size, preserveAspect, dx, dy) {    // 10行重复的数学运算代码  },};
let Oval = { resizeLeft(position, size, preserveAspect, dx, dy) { // 10行重复的数学运算代码 }, resizeRight(position, size, preserveAspect, dx, dy) { // 10行重复的数学运算代码 }, resizeTop(position, size, preserveAspect, dx, dy) { // 10行重复的数学运算代码 }, resizeBottom(position, size, preserveAspect, dx, dy) { // 10行重复的数学运算代码 },};
let Header = { resizeLeft(position, size, preserveAspect, dx, dy) { // 10行重复的数学运算代码 }, resizeRight(position, size, preserveAspect, dx, dy) { // 10行重复的数学运算代码 }, }
let TextBlock = { resizeTopLeft(position, size, preserveAspect, dx, dy) { // 10行重复的数学运算代码 }, resizeTopRight(position, size, preserveAspect, dx, dy) { // 10行重复的数学运算代码 }, resizeBottomLeft(position, size, preserveAspect, dx, dy) { // 10行重复的数学运算代码 }, resizeBottomRight(position, size, preserveAspect, dx, dy) { // 10行重复的数学运算代码 },};
复制代码


这些重复代码看起来真的很碍眼。


这样的代码不够干净。


大部分重复是因为朝相同方向调整形状的代码都差不多,比如 Oval.resizeLeft()和 Header.resizeLeft()就很类似。


其他重复是因为同一种形状的方法之间很相像,比如 Oval.resizeLeft()和 Oval 其他的方法就很类似。另外,Rectangle、Header 和 TextBlock 之间也有重复的地方,因为文本框也是矩形。


我想到了一个办法。


我们可以给代码分组,把重复代码移除掉,比如像下面这样。


let Directions = {  top(...) {    // 5行不一样的数学运算代码  },  left(...) {    // 5行不一样的数学运算代码  },  bottom(...) {    // 5行不一样的数学运算代码  },  right(...) {    // 5行不一样的数学运算代码  },};
let Shapes = { Oval(...) { // 5行不一样的数学运算代码 }, Rectangle(...) { // 5行不一样的数学运算代码 },}
复制代码


然后,把它们的行为组合起来。


let {top, bottom, left, right} = Directions;
function createHandle(directions) { // 20行代码}
let fourCorners = [ createHandle([top, left]), createHandle([top, right]), createHandle([bottom, left]), createHandle([bottom, right]),];let fourSides = [ createHandle([top]), createHandle([left]), createHandle([right]), createHandle([bottom]),];let twoSides = [ createHandle([left]), createHandle([right]),];
function createBox(shape, handles) { // 20行代码}
let Rectangle = createBox(Shapes.Rectangle, fourCorners);let Oval = createBox(Shapes.Oval, fourSides);let Header = createBox(Shapes.Rectangle, twoSides);let TextBox = createBox(Shapes.Rectangle, fourCorners);
复制代码


代码量减少了一半,重复代码完全消失了!多么干净。如果要修改某个形状或方向的行为,只需要在一个地方做出改动,不需要修改所有的方法。


夜已深,我把改好的代码提交到 master 分支,然后上床睡觉。因为帮同事把杂乱的代码清理干净了,我心里还引以为豪。


第二天

事情并没有像我期待的那样发生。


老板找我谈话,他们希望我把代码回滚回去。我感到很惊讶,毕竟原先的代码简直就是一团乱麻,而我改得很干净啊!


我很不情愿地答应了,但几年之后,我才意识到他们其实是对的。


必经之路

痴迷于“干净代码”和删除重复代码是我们很多人都会经历的一个阶段。当我们对自己的代码不是很自信时,就很容易将自我价值感和职业自豪感与一些可以被衡量的东西联系在一起,比如严格的 lint 规则、命名模式、文件结构、不重复代码实践。


我们没办法自动去除重复代码,但可以自己动手做。每次修改代码之后,我们可以很容易地知道重复代码是少了还是多了。所以,去除重复代码感觉就像是在改进代码质量。更糟糕的是,它扰乱了人们的认同感,让他们觉得“我是那种编写干净代码的人”,但这其实无异于自我欺骗。


一旦学会了抽象,我们就很容易对这种能力产生很高的期望,每当看到有重复代码就会想要对它们进行抽象。在写了几年代码之后,我们发现重复代码到处都是,而抽象成了我们获得的一项超级能力。如果有人告诉我们说抽象是一种美德,那我们肯定会深信不疑,并且会因为别人不崇尚“干净代码”而对他们品头论足。


现在,我知道之前的代码重构就是一个灾难,原因如下。


  • 首先,我没有事先和写代码的人沟通。我直接修改了他们的代码并提交,没有和他们讨论。即使这是一种改进(但我现在不这么认为了),但我这样的行事方式并不值得称道。一个健康的工程团队应该以信任为基础,不经过讨论就修改他人的代码会对团队协作造成沉重的打击。

  • 其次,天下没有免费的午餐。我以牺牲灵活性为代价,以此来减少重复代码,这算不上是一个好的权衡。例如,后来我们要求不同形状的不同手柄具备一些特殊的行为,被我重构过的代码需要修改多次才能满足需求,而原先“杂乱”的代码却可以很容易实现这些需求。


那么,我的意思是我们应该尽量写“脏”代码吗?当然不是。我只是建议大家在考虑什么是“干净”或“脏”代码时进行深度思考。你当时有什么样的感觉?厌恶?正义?美丽?优雅?你可以肯定这些品质会带来实质性的工程成果吗?它们又是如何影响代码的编写和修改方式的?


我确实没有深入思考过这些事情。我只考虑到代码本身,但从来没有想过代码与团队之间的演化关系。


编码就像是一段旅程,想想你从写第一行代码到现在走了多远。当第一次通过提取函数或重构类让复杂的代码变简单,我觉得那是一种乐趣。如果你对自己的“杰作”感到自豪,那么就很容易掉入追求干净代码的旋涡。


但请不要止步于此,不要只做一个干净代码狂热者。写出干净的代码并不是我们的终极目标,我们只是通过这种方式尝试找到处理系统复杂性的方法。当你不确定代码改动会对代码库造成怎样的影响,在未知的海洋中需要灯塔的指引,那么这不失为一种防御机制。


写出干净的代码可以作为一种指引,但后面的路还是要自己走。


原文链接

Goodbye clean code!


2020 年 2 月 06 日 14:5212465
用户头像
小智 前 InfoQ 主编

发布了 408 篇内容, 共 347.5 次阅读, 收获喜欢 1905 次。

关注

评论 6 条评论

发布
用户头像
有道理,虽然代码目前看着一样,但是如果含义和应用场景不同的话,迟早要分化,不要一开始就瞎jb整在一起
2020 年 09 月 16 日 11:45
回复
用户头像
对于第二点原因不太认同。我的经验是,当工程达到一定规模,代码越多反而灵活性越差
2020 年 02 月 18 日 13:15
回复
用户头像
如果是技术leader的话,这件事是需要先跟下面人一起讨论如何去优化代码,而不是自己去做,这种行为就是一种自私不信任,不然下面的人来了是当摆设,来衬托自己很牛吗?如果是员工的话,只需要负责好自己的那部分代码就可以了,因为代码的整体执行方案还是需要技术leader去领导的,不然百花齐放,每个人心里都有自己的完美
2020 年 02 月 17 日 09:39
回复
用户头像
直接改是不太好,最好能提PR,讨论之后再合并,不然别人维护了一年的代码,被你随手改的妈妈都不认识了,换谁不一肚子火。
2020 年 02 月 15 日 23:54
回复
用户头像
这个理由有牵强得一笔。。。继承一个实现接口,使用装饰器不就好了
2020 年 02 月 09 日 09:55
回复
你读过人家的源码?
2020 年 02 月 11 日 16:17
回复
没有更多了
发现更多内容

剑指Offer——企业级项目中分层的含义与依据及多态的优势

No Silver Bullet

封装 offer 继承 多态 2月月更

Spring Boot Serverless 实战系列 | 性能调优

阿里巴巴云原生

阿里云 Serverless 架构 云原生

企业为什么要做应用多活?

阿里巴巴云原生

阿里云 云原生 容灾

平安保险基于 SPI 机制的 RocketMQ 定制化应用

阿里巴巴云原生

阿里云 云原生 消息队列 开源云工具

计算机网络面试知识点

yuexin_tech

面试 计算机网络

Kotlin语法手册(三)

寻找生命中的美好

android kotlin 安卓

KubeDL HostNetwork:加速分布式训练通信效率

阿里巴巴云原生

阿里云 云原生 分布式训练 KubeDL

云原生落地大爆发,企业和开发者如何把握先机?

阿里巴巴云原生

阿里云 云原生 ACK ACK Anywhere

为什么start方法不能重复调用?而run方法却可以?

王磊

Java 面试

虎啸春来!丰树电子与中联重科签署战略合作协议

联营汇聚

人人都是 Serverless 架构师 | 现代化 Web 应用开发实战

阿里巴巴云原生

阿里云 Serverless 云原生

阿里云容器服务差异化 SLO 混部技术实践

阿里巴巴云原生

阿里云 Kubernetes 云原生 混部技术

国内唯一!阿里云容器服务进入 Forrester 领导者象限

阿里巴巴云原生

阿里云 云原生 容器平台

《MySQL入门很轻松》第4章:数据表中能存放的数据类型

乌龟哥哥

数据库 2月月更

详细讲解mybatis的执行流程

编程江湖

mybatis

流量录制与回放在vivo的落地实践

vivo互联网技术

测试工具 回归测试 流量回放

如何快速构建服务发现的高可用能力

阿里巴巴云原生

阿里云 开源 微服务 云原生

专访 OpenKruise 负责人:现在的云原生应用自动化发展到什么程度了?

阿里巴巴云原生

阿里云 开源 云原生

Go 语言Web开发很简单:使用模板将视图与逻辑分离

宇宙之一粟

Go 语言 Web应用开发 2月月更

甜言蜜语生成器、定时问候邮件机…开源程序员为这个情人节付出太多

腾源会

开源

还没有表白神器?情人节来喽,快为心爱的她送上一份专属的礼物吧~

是Dream呀

Python 2月月更

美团动态线程池实践思路,开源了

yanhom

Java 线程池 动态调整线程池参数 动态线程池 美团线程池

SSH远程连接命令执行没反应不报错问题解决(-bash: fork: retry: Resource temporarily unavailable.[资源暂时不可用])

山河已无恙

SSH Linxu 2月月更

Linux系统编程-进程间通信(mmap内存映射)

DS小龙哥

2月月更 mmap内存映射

DGIOT 工业物联网开源平台简介

dgiot

物联网 2月月更 2月日更 dgiot dgiot物联网

教程直播第8期|一文详解 OceanBase 社区版生态工具 ODP & OCP

OceanBase 数据库

数据库 分布式 直播 OceanBase 开源

运营给产品送的情人节礼物是?

阿里云弹性计算

产品运营 情人节 轻量征文 用户投稿

独家下载!阿里云云原生携 10+ 技术专家带来《云原生与云未来的新可能》

阿里巴巴云原生

阿里云 Kubernetes 云原生 电子书

微信朋友圈高性能架构方案

Geek_36cc7c

给面试加点硬菜:延迟任务场景,该如何提高吞吐量和时效性!

小傅哥

小傅哥 架构设计 Redis 6.0 任务系统

DevOps进阶(三)走近 DevOps 工程师

No Silver Bullet

DevOps 敏捷 jenkins 2月月更

金融行业数据库架构实践与运维 | DBTalk 技术公开课第2期

金融行业数据库架构实践与运维 | DBTalk 技术公开课第2期

把同事的代码重写得干净又整洁,老板却让我做回滚?_文化 & 方法_Dan Abramov_InfoQ精选文章