【翻译】再见, Clean Code!


鑫宝Code

🌈个人主页: 鑫宝Code
🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础
💫个人格言: "如无必要,勿增实体"


正文开始

文章目录

  • 【翻译】再见, Clean Code!
    • 正文
      • 那是一个深夜
      • 次日早晨
      • 这只是一个阶段

【翻译】再见, Clean Code!

这篇文章翻译于React核心开发者Dan的这篇博客。

原文链接:overreacted.io/goodbye-cle…

正文

那是一个深夜

我的同事刚刚提交了他们整个星期一直在编写的代码。我们正在开发一个图形编辑画布,他们实现了通过拖动矩形和椭圆等形状边缘的小手柄来调整其大小的功能。

代码能正常运行。

但显得很冗余。每个形状(如矩形或椭圆)拥有各自不同的手柄集合,而拖动手柄的不同方向会以不同的方式影响形状的位置和尺寸。如果用户按住 Shift 键,我们还需要在调整大小时保持形状的长宽比。涉及了大量的数学计算。

代码大致如下:

let Rectangle = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
};

let Oval = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeTop(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottom(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
};

let Header = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },  
}

let TextBlock = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
};

那些重复的数学运算令我颇为烦恼。

代码不够整洁。

大部分重复出现在处理相似方向的函数之间。比如,Oval.resizeLeft()Header.resizeLeft() 就有相似之处,因为它们都涉及拖动左侧的手柄。

另一类相似性存在于处理相同形状的所有方法之间。例如,Oval.resizeLeft() 与其它 Oval类的其他方法也有共通之处,因为它们都是围绕椭圆进行操作。同样,RectangleHeaderTextBlock 之间也存在一些重复,因为文本块本质上就是矩形。

我有了一个想法。

我们可以这样对代码进行归类,从而消除所有重复:

let Directions = {
  top(...) {
    // 5 unique lines of math
  },
  left(...) {
    // 5 unique lines of math
  },
  bottom(...) {
    // 5 unique lines of math
  },
  right(...) {
    // 5 unique lines of math
  },
};
 
let Shapes = {
  Oval(...) {
    // 5 unique lines of math
  },
  Rectangle(...) {
    // 5 unique lines of math
  },
}

然后组成他们的行为

let {top, bottom, left, right} = Directions;
 
function createHandle(directions) {
  // 20 lines of code
}
 
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 lines of code
}
 
let Rectangle = createBox(Shapes.Rectangle, fourCorners);
let Oval = createBox(Shapes.Oval, fourSides);
let Header = createBox(Shapes.Rectangle, twoSides);
let TextBox = createBox(Shapes.Rectangle, fourCorners);

代码的总大小减半,重复的部分也完全消失了!如此整洁。如果我们想要改变某个特定方向或形状的行为,我们可以在一个地方进行修改,而不是到处更新方法。

已经是深夜了(我太投入了)。我将我的重构代码提交到了主分支然后去睡觉了,为自己解开了同事混乱的代码而感到骄傲。

次日早晨

……并未如我所料。

上司找我进行了一次单独交谈,委婉地要求我撤销那次修改。我惊愕不已。旧代码一团糟,而我的代码整洁明了!

尽管心有不甘,我还是照做了。然而,我花了好几年才意识到他们是对的。

这只是一个阶段

对“清洁代码”痴迷、热衷于消除重复,是我们许多人必经的一个阶段。当我们对自己的代码缺乏信心时,往往会将自己的自我价值感和职业自豪感寄托于那些可度量的事物上。一套严格的代码风格规则、一种命名方案、一种文件结构、对重复的零容忍……

虽然无法完全自动化地消除重复,但随着练习,这一过程会变得愈发容易。通常情况下,每次修改后,你都能判断出代码中的重复是增多了还是减少了。因此,消除重复仿佛是在提升代码某个客观指标,给人以成就感。更糟糕的是,它还会影响人们的自我认知:“我是那种编写清洁代码的人”。这种错觉具有极强的迷惑性。

一旦我们掌握了创建抽象的能力,就很容易对此上瘾,只要看到重复的代码,就会迫不及待地从中抽离出抽象。经过几年编程历练,我们会发现到处都是重复——而抽象化正是我们的新超能力。如果有人告诉我们抽象是一种美德,我们会欣然接受,并开始评判他人不崇尚“清洁”。

我现在明白,那次所谓的“重构”在两方面都是一场灾难:

  • 首先,我没有与原作者沟通。我在没有征得他们意见的情况下重写了代码并提交。即便这算是一种改进(我现在已不再这么认为),这种方式也极其糟糕。一个健康的工程团队始终在建立信任。未经讨论就擅自重写队友的代码,将严重损害你们在代码库上的协作效率。
  • 其次,没有什么是免费的。我的代码牺牲了应对需求变更的能力,换取了减少重复,但这并非一笔划算的交易。例如,后来我们需要为不同形状的不同手柄添加许多特例和行为。若沿用我的抽象设计,实现这些变更会复杂数倍;而若是采用原先“杂乱”的版本,这些改动则轻而易举。

那我是否在建议你应该编写“脏”代码呢?并非如此。我想强调的是,当你谈论“清洁”或“脏乱”时,应当深入思考其含义。这些词语会让你产生反感、正义感、美感或是优雅感吗?你能否确切指出这些品质所对应的工程成果?它们又是如何具体影响代码的编写与修改方式?

我当初的确未曾深入思考这些问题,只是过分关注代码的外观,却忽视了它在一个由充满变数的人类组成的团队中如何演变。

编程是一段旅程。想一想从写下第一条代码至今,你已走了多远。第一次体验到通过提取函数或重构类让复杂代码变得简洁,想必令你欣喜不已。如果你对自己的技艺引以为豪,追求代码清洁自然颇具吸引力。那么,就先这样做一段时间吧。

但切勿止步于此。不要成为清洁代码的狂热信徒。清洁代码并非目标,而是我们在面对系统无尽复杂性时试图理清头绪的一种手段,是在面对未知领域、不清楚某项改动会对代码库产生何种影响时的导航工具。

让清洁代码指引你,然后适时放下它。

个人感言:clean code固然重要,但不可过度封装🙅‍♂️。

End

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/542700.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【植物大战僵尸融合机器学习】+源码

上期回顾: 今天给大家推荐一个Gtihub开源项目:PythonPlantsVsZombies,翻译成中就是植物大战僵尸。 《植物大战僵尸》是一款极富策略性的小游戏。可怕的僵尸即将入侵,每种僵尸都有不同的特点,例如铁桶僵尸拥有极强的抗…

【设计模式学习】单例模式和工厂模式

꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转…

Java-博客系统(前后端交互)

目录 前言 博客系统基本情况 1 创建项目,引入依赖 2 数据库设计 2.1 分析 2.2 建库建表 3 封装数据库 3.1 在java目录下创建DBUtil类,通过这个类对数据库进行封装 3.2 在java目录下创建实体类(博客类Blog) 3.2 在java目录下创建…

vwmare+Ubuntu20.04安装超级保姆级完整教程

强烈建议先完整的看完一遍教程在进行安装以免出现问题!!! 如果遇到error:建议复制error后面的信息然后到浏览器搜索,查找解决方案,其次在进行某个不确定的操作时,建议先保存快照,这样…

uboot操作指令1

文章目录 前言一、信息查询命令1.bdinfo用于查看板子的信息2.printenv 打印环境变量3.version查看uboot版本 二、环境变量操作命令1.setenv修改环境变量2.setenv新建环境变量3.setenv删除环境变量 三、内存操作命令1.md 命令2.nm命令3.mm命令4.mw命令 四、网络操作命令1.ping命…

Zookeeper与kafka

目录 一、zookeeper 1.1.zookeeper概述 1.2.Zookeeper 工作机制 1.3. Zookeeper 特点 1.4.Zookeeper 数据结构 1.5.Zookeeper 应用场景 1.6.Zookeeper 选举机制 第一次启动选举机制 非第一次启动选举机制 选举Leader规则: 1.7.部署 Zookeeper 集群 1.7.…

AI人工智能讲师大模型培训讲师叶梓 大语言模型(LLM)在科学文献摘要领域的应用

大语言模型(LLM)在科学文献摘要领域的应用是一个前沿且迅速发展的技术趋势。通过结合GitHub上yobibyte的Compressor项目,我们可以深入探讨这一技术方案的潜力和实现方式。 技术背景 随着科学研究的快速发展,每天都有大量的科学文…

matlab学习(三)(4.9-4.15)

一、空域里LSB算法的原理 1.原理: LSB算法通过替换图像像素的最低位来嵌入信息。这些被替换的LSB序列可以是需要加入的水印信息、水印的数字摘要或者由水印生成的伪随机序列。 2.实现步骤: (1)将图像文件中的所有像素点以RGB形…

服务器数据恢复—ext3文件系统下raid5数据恢复案例

服务器数据恢复环境&故障情况: 某企业光纤存储上有一组由16块硬盘组建的raid5阵列。管理员发现该光纤存储上的卷无法挂载,经过检查发现raid5阵列中有2块硬盘离线,于是联系我们数据恢复中心要求数据恢复工程师到现场恢复服务器存储上的数据…

【可能是全网最丝滑的LangChain教程】七、LCEL表达式语言

系列文章地址 【可能是全网最丝滑的LangChain教程】一、LangChain介绍-CSDN博客 【可能是全网最丝滑的LangChain教程】二、LangChain安装-CSDN博客 【可能是全网最丝滑的LangChain教程】三、快速入门LLM Chain-CSDN博客 【可能是全网最丝滑的LangChain教程】四、快速入门Re…

在js中计算两个时间段重叠的时长问题

文章目录 前言一、过程分析二、实现代码(js)总结 前言 最近遇到一个需求,就是在js中计算两段时间的重叠时长问题,这里记录一下。 一、过程分析 两段时间的重叠问题,一般有3中情况 两段时间完全无重叠,也就是无任何交集两段时间…

软考中级--网络工程师-计算机基础与理论第二节无线基础知识

IEEE802.11 规定了多种 WLAN 通信标准,其中( )与其他标准采用的频段不同,因而不能兼容。 A IEEE802.11a B IEEE802.11b C IEEE802.11g D IEEE802.11n 试题答案 正确答案: A 答案解析 IEEE 802.11a规定采用5GHz的 ISM频…

007Node.js安装自启动工具supervisor运行js文件

在vscode中,某些运行中的程序修改xx.js文件后,通过CtrlC终止再重新运行。supervisor是自启动工具,会不停的查看你的文件,一旦发现有修改,就立马重新载入运行。 我们可以通过安装supervisor代替node命令运行xx.js。终端…

环境变量与进程优先级

目录 进程的优先级 什么是优先级 为什么要有优先级 linux的优先级特点和查看方式 其他概念 环境变量 命令行参数 环境变量 查看环境变量方法 修改PATH 其他环境变量 进程的优先级 什么是优先级 优先级:指定进程获得某种资源的先后顺序。(优先级…

Python数据分析案例40——电商直播间成交金额预测

承接上一篇案例电商直播间提取的特征,进而做一篇机器学习的案例,来预测直播间的成交金额。 Python数据分析案例39——电商直播间评论可视化分析(LDA) 1. 引言 1.1 直播电商与传统电商的比较 直播电商作为一种新兴的电子商务模式…

c语言中<string.h>的strstr与strtok函数

c语言中string.h的strstr与strtok函数 代码运行结果 代码 #include <stdio.h> #include <string.h>///1.在字符串str1里面,查找第一次出现str2的位置 //char * strstr(const char * str1,const char * str2)///2.sep为分割符,根据分割符来对str进行分割 //char * …

【WEEK7】 【DAY5】JDBC—PreparedStatement Object【English Version】

2024.4.12 Friday Following 【WEEK7】 【DAY4】JDBC—Statement Object【English Version】 Contents 10.3.PreparedStatement Object10.3.1.PreparedStatement can prevent SQL injection, more efficient than statement10.3.2. Insertion10.3.3. Deletion10.3.4. Update10.…

Windows版PHP7.4.9解压直用(免安装-绿色-项目打包直接使用)

安装版和解压版 区别 安装版: 安装方便&#xff0c;下一步------下一步就OK了&#xff0c;但重装系统更换环境又要重新来一遍&#xff0c;会特别麻烦解压版&#xff08;推荐&#xff09;&#xff1a; 这种方式&#xff08;项目打包特别方便&#xff09;能更深了解mysql的配置&…

C 408—《数据结构》易错考点200题(含解析)

目录 Δ前言 一、绪论 1.1 数据结构的基本概念 : 1.2 算法和算法评价 : 二、线性表 2.2 线性表的顺序表示 : 2.3 线性表的链式表示 : 三、栈、队列和数组 3.1 栈 3.2 队列 3.3 栈和队列的应用 3.4 数组和特殊矩阵 四、串 4.2 串的模式匹配 五、树与二叉树 5.1 树的基…

StarUML笔记之从UML图生成C++代码

StarUML笔记之从UML图生成C代码 —— 2024-04-14 文章目录 StarUML笔记之从UML图生成C代码1.Add Diagram2.在TOOLBOX中左键点击Class,松开,然后在中间画面再左键点击&#xff0c;即可出现UML3.修改类图&#xff0c;并添加接口&#xff0c;方法&#xff0c;属性&#xff0c;我…