编写简洁代码(Clean Code)不是一件非常容易的事情,为了保持代码整洁,你需要尝试不同的技巧和做法。问题是,在这个问题上,有太多的做法和技巧,是需要大量的重构的。因此,开发者很难选择出适合自己项目的做法和技巧。
让我们来简化这个任务,在本文中,我们将讨论编写整洁代码的一些好处,然后再来看看我们总结的 6 个技巧和实践,以便让开发人员理解,编写 Clean Code 最常使用的一些技巧。
一、编写简洁代码的好处
让我们先看看编写简洁干净的代码有什么好处。其中一个主要的好处是,简洁干净的代码可以帮助我们最大限度的减少阅读和试图理解代码的时间。凌乱的代码风格,具有不可思议的能力,可以加大开发人员的工作效率,让它们的工作变的更加困难。代码越是混乱,开发人员维护起来需要的时间越多。而且,如果代码太乱,维护的开发人员可能会决定停下来,然后重构它。
1.1 更容易开始,也更容易继续
让我举一个简单的例子来说明这一点。
假设我们在很长一段时间之后,重新维护我们之前开发的某一个项目。也许,是我们以前的某个客户,联系了我们并重新需要我们对其进行继续的扩展与维护。
现在,我们可以想像到,如果在项目开发阶段,我们没有使用简洁代码的技巧编写简洁的代码,而是写下了相反的代码。你将第一次知道,你的代码是多么的糟糕和混乱。而且这个时候你也会发现,重新维护一个之前的项目,是多么的艰难。
因此,我们现在必须在这个项目上,花费更多的时间,因为我们需要重新了解我们之前编写的代码。而这并不是必要的,我们可以从一开始就坚持编写简洁代码,来完全避免它。
现在我们必须增加成本,而且,我们的旧代码如此的混乱不堪,如果我们决定从头开始。我想这不是你的客户们乐意听到的,因为这代表了成本的增加和工期的延长。
Clean Code 就不会有这样的问题,想像一下前面的例子,情况就会刚好相反。现在,我们之前的代码是干净且优雅的。那我们需要多长时间才能理解它?也许只需要几分钟,简单阅读一下代码,了解之前是如何工作的。最后我们在这个基础上,花费一段时间进行维护或者扩展新的功能。这次改动投资的成本和时间,将会小得多,我们的客户可能都不会注意到你做了什么。
这就是我们讨论坚持使用 Clean Code 编码技巧的第一个好处。而且,这不仅仅是为了我们自己维护的项目,也是为了其他开发者。Clean Code 能够让我们更快的上手,我们或者其他开发者,都不需要花费数个小时的时间来研究它。我们可以更快的进入到工作之中。
1.2 更容易让人加入团队
Clean Code 的另一个好处是与第一个原因密切相关的,它将加快新人接手项目的速度。
我的意思是,假设我们需要聘请另外一个开发人员,需要多长时间才能理解代码并学习如何使用它?这取决于,如果我们的代码本身很混乱,写的不好,他将需要更多的时间来完成首次接手的工作。而如果我们的代码是保持简洁的、易读的,这将非常的易于理解,那么他就能更快的接手并且开始编写新的需求。
有些人可能觉得这不是一个问题,因为我们这些作者还在这里,我们可以帮助他更快的理解,虽然这真的可以加快他的上手速度。但是,我们的帮助只应该花费我们很短暂的时间,一两天或者三天,而不应该是一周或者很频繁的。
当我们决定招募另一位开发人员,是为了加速我们的项目研发,而不是放缓它。我们的目标不是帮助他学习如何在我们之前编写的代码之上继续编码,这将会消磨我们更多的时间。
1.3 更容易保持规范
有一件事情我们需要注意,了解和学习如何编写代码是一回事。但是,这只是一个开始,我们还需要确保开发者能够并且愿意遵循我们的编码规范,并将它持续的实践在项目中。这将能保持项目是持续的干净整洁,而不会因此而凌乱下去。这一点很重要,因为我们不仅要编写简洁的代码,还要保持这样的代码,不管将来有多少人会来维护它,我们需要长期坚持和思考这个问题。
最后,如果我们团队的开发者之一,决定不遵循当前的编码规范呢?
通常这个问题会被自行解决。假设我们有一群人在相同的项目代码上进行工作,并且开始偏离既定的代码规范。这可能会导致三个方向的发展。
首先,团队的其他成员会推动这名开发人员遵循标准,如果他不想离开团队,他会接收的。
第二个方向是,开发人员能够说服团队的其他成员采用并遵循他的编码规范。如果开发人员提出的编码规范是更简洁的,能带来更好的结果,这应该是一件好事情。编写并保持我们的代码简洁,并不意味着我们应该忽略任何改进它的机会。恰恰相反,我们相信我们总是应该质疑我们目前的做法,并寻求改善的机会。
所以,如果一个开发者偏离了我们的想法,而且他的做法更好,这样的情况下,我们做出改变,可能是最好的结果,而不是让他来改变。我认为,我们审视和尝试之前,我们不应该忽视别人的方法。事情总有改进的余地,我们应该持续的寻找改进的方法,而不是固守成规。
最后一种也是我们最不愿意看到的,开发者决定不采用我们的规范,也不尝试说服我们采用他的规范。这可能导致他离开团队。
二、编写 Clean Code 的一些建议
之前,我们在讨论编写简洁代码的一些好处,现在是时候学习一些技巧来帮助我们做到这一点。
正如我们将要看到的,拥抱简洁的代码并遵循这些建议进行实践下去。这些我们总结的做法,会使我们的代码更加清晰、可读,更简单并且易于理解。
当然,你并不需要实践这些所有的建议,实践并且遵循一两个,就足以代码有效的成果。
2.1 让代码可读
是的,我们编写的代码,最终会被机器解释。但是,这并不意味着我们应该忽略它的可读性和可理解性。总会有另外的开发者有机会阅读我们的代码,否者将无法维护它。即使作为独立开发者坚持自己维护代码,我们也可能会在将来继续维护我们的项目。出于这些原因,我们应该保持我们代码的可读性。
最简单的方法是使用空格进行格式化,我们在发布代码之前,将代码压缩(混淆)是可以的。但是我们并不需要编写看起来像是被压缩的代码。相反,我们可以使用缩进、换行和空行,来使代码的结构更具有可读性。当我们决定采取这个做法的时候,我们代码的可读性和可理解性可以显著提高。
接下来,看一下我们的示例代码,就应该能够理解它了。
Code1:
- // Bad
- const userData=[{userId: 1, userName:
- 'Anthony Johnson', memberSince: '08-01-2017',
- fluentIn: [ 'English', 'Greek', 'Russian']},
- {userId: 2, userName: 'Alice Stevens',
- memberSince: '02-11-2016',
- fluentIn: [ 'English', 'French', 'German']},
- {userId: 3, userName: 'Bradley Stark',
- memberSince: '29-08-2013',
- fluentIn: [ 'Czech', 'English', 'Polish']},
- {userId: 4, userName: 'Hirohiro Matumoto',
- memberSince: '08-05-2015', fluentIn: [ 'Chinese',
- 'English', 'German', 'Japanese']}];
- // Better
- const userData = [
- {
- userId: 1,
- userName: 'Anthony Johnson',
- memberSince: '08-01-2017',
- fluentIn: [
- 'English',
- 'Greek',
- 'Russian'
- ]
- }, {
- userId: 2,
- userName: 'Alice Stevens',
- memberSince: '02-11-2016',
- fluentIn: [
- 'English',
- 'French',
- 'German'
- ]
- }, {
- userId: 3,
- userName: 'Bradley Stark',
- memberSince: '29-08-2013',
- fluentIn: [
- 'Czech',
- 'English',
- 'Polish'
- ]
- }, {
- userId: 4,
- userName: 'Hirohiro Matumoto',
- memberSince: '08-05-2015',
- fluentIn: [
- 'Chinese',
- 'English',
- 'German',
- 'Japanese'
- ]
- }
- ];
Code2:
- // Bad
- class CarouselLeftArrow extends
- Component{render(){return
- ( <a href="#" className="carousel__arrow carousel__arrow--left"
- onClick={this.props.onClick}> <span className="fa fa-2x
- fa-angle-left"/> </a> );}};
- // Better
- class CarouselLeftArrow extends Component {
- render() {
- return (
- <a
- href="#"
- className="carousel__arrow carousel__arrow--left"
- onClick={this.props.onClick}
- >
- <span className="fa fa-2x fa-angle-left" />
- </a>
- );
- }
- };
2.2 为变量、方法取有意义的名称
再让我们来看看第二个技巧,这将帮助我们编写易于理解的代码。
这个技巧是关于变量、函数和方法使用有意义的名称。有意义是什么意思?有意义的名称是具有描述性的名称,不仅仅是我们,其他人也能够看名称就能理解,这些变量、函数和方法的目的。
换句话说,名称本身应该就可以提示变量、函数和方法的用途,或者它包含的内容。
接下来看个例子。
- // Bad
- const fnm = ‘Tom’;
- const lnm = ‘Hanks’
- const x = 31;
- const l = lstnm.length;
- const boo = false;
- const curr = true;
- const sfn = ‘Remember the name’;
- const dbl = [‘1984’, ‘1987’, ‘1989’, ‘1991’].map((i) => {
- return i * 2;
- });
- // Better
- const firstName = ‘Tom’;
- const lastName = ‘Hanks’
- const age = 31;
- const lastNameLength = lastName.length;
- const isComplete = false;
- const isCurrentlyActive = true;
- const songFileName = ‘Remember the name’;
- const yearsDoubled = [‘1984’, ‘1987’, ‘1989’, ‘1991’].map((year) => {
- return year * 2;
- });
但是,我们应该知道,使用描述性名称并不意味着我们可以自由使用尽可能多的单词来描述它。一个好的经验法则是将名称限制在三个或者四个单词。如果我们需要四个以上的单词才能描述它,这也许是因为我们一次尝试让它做太多的事情,这个时候我们应该简化我们的代码,而不是坚持使用它。
2.3 让一个函数或者方法,只执行一个任务
当我开始写代码的时候,我曾经写过类似瑞士军刀的功能和方法。它们可以处理和做任何事情。但是到了后期,其中的一个后果就是很难再找到一个好名字来扩展它。其次,除了我之外,几乎没有人知道这个和那个功能是什么以及如何使用它,有时候我自己也会面临这个问题,所以我不得不写下注释。第三,这些功能是不可预测的,而我将因此写了一段混乱的代码。
然后,有人给了一个伟大的建议,让每个功能或方法只执行一项任务。这个简单的建议改变了一切,并帮助我编写简洁的代码,至少比之前更加干净简洁。从那一刻开始,其他人终于能够理解我的代码了。或者说,他们不需要像以前一样,花费那么多的时间来理解我的代码。我的功能和方法也变得可预测。在相同的输入下,它们总是产生相同的输出。而且,命名也变得更容易。
如果你很难为你的函数和方法找到描述性名称,或者你需要编写冗长的注释以便其他人可以使用它们,请考虑实时单一职责。让每个函数或方法只执行一项任务。如果你的函数和方法看起来像瑞士军刀一样大而全,也可以实施这种做法。相信我,在编写代码上,任何多功能性都不是一个优势。任何时候都可能会导致适得其反的效果,这是一个相当不利的因素。
注意:让每个函数或方法只执行一项任务的做法称为单一责任原则。罗伯特·C·马丁(Robert C. Martin)将这种编码实践作为五种面向对象设计原理之一(也称为SOLID)进行了介绍。
- // Example no.1: 简单的减法
- function subtract(x, y) {
- return x - y;
- }
- // Example no.1: 简单的乘法
- function multiply(x, y) {
- return x * y;
- }
- // Example no.1: Double numbers in an array
- function doubleArray(array) {
- return array.map(number => number * 2)
- }
2.4 写好注释
不管我们为自己的变量,函数和方法,绞尽脑汁的想出有意义的名称。我们的代码本身可能还是有一些不清晰或者不易于理解的地方。可能是有一些逻辑分支需要单独解释,这些可能不是它们很难理解和使用的原因。相反可能只是一些历史遗留问题。
有时我们可能不得不采取非常规的方式来解决问题,因为没有别的办法可行,或者我们没有足够的时间来提出更好的解决方案。
这可能很难用代码解释。通过在我们的代码中,增加注释可以帮助我们解决这个问题。注释可以帮助我们向其他人解释为什么我们写了这段不合规的代码,以及我面临的问题和现状。因此,其他人在阅读的时候就很清晰,不必猜测我的当时的想法。
更重要的是,当我们解释我们的原因时,其他人可能会找到一个更好的方法来解决问题并改进代码。这是可能的,因为他们可能知道问题是什么,期望的结果是什么。如果不知道这些信息,其他人就难以创造更好的解决方案。否者,其他人将不会尝试优化解决它,因为他们不认为有这个需要,他们可能认为这就是我们真实的想法。
所以,当我们发现自己处于一种我们决定使用一些非常规的方式,快速修复或绕过问题方法的来解决问题情况下,我们就应该用注释来解释为什么我们面临的是什么问题,为什么要这么做。用一两行注释来解释它比强迫其他人猜测更好。
也就是说,我们应该只在有必要时才使用注释,而不是解释错误的代码。不停的增加注释,并不会帮助我们将写得差的代码转换成干净简洁的代码。如果代码不好,我们应该通过改进代码来解决问题,而不是通过添加关于如何使用代码的注释。简洁的代码优先于使用注释解释。
2.5 保持一致
当我们找到我们喜欢的具体的编码实践或风格时,我们应该坚持并在任何地方使用它。在不同的项目中使用不同的编码规范或样式不是一个好主意。它几乎与不使用任何编码规范或风格一样无用。这将导致,回到我们的旧代码将不会如此平稳和自然。我们仍然需要一些时间来理解我们在该项目上使用的编码规范,然后才能使用它。
最好的办法是挑选一套编码规范,然后在我们所有的项目中坚持这些规范。那么,回到我们以前的代码会更容易,并继续我们停止或改进的地方。尝试新的编码规范是一件好事。它可以帮助我们找到更好的方式来完成我们的工作。
但是,在挑选编码规范的时候,我们应该多做一些尝试,并且实际使用在多个地方。我们应该花精力去验证我们的规范,来保证它整的适合我们,只有在我们对此感到满意的时候,我们才应该实施它。并且在全部项目中,使用这套规范。
2.6 定义检查代码,适时重构
这是我编写简洁的代码的最后一个技巧。
简单地写简洁的代码并不是一切。我们的工作并不因为我们使用了简洁的风格编写代码而结束。下一步是保持我们的代码的简洁。简洁的代码需要维护,当我们写代码的时候,我们应该定期回视检查,清理垃圾代码并进行改进。否则,如果我们不检查和更新旧代码,它很快就会过时。就像我们的硬件设备一样。如果我们想要保持最佳状态,我们需要定期更新它们。
对于我们每天使用的代码来说,情况尤其如此。代码有变得越来越复杂和混乱的趋势,而不是简单和整洁。我们要防止这种情况发生,并保持我们的代码的简洁。要做到这一点的唯一方法是定期检查我们的代码。换句话说,我们需要维护它。对于我们不再关心或没有前途的项目来说,这可能不是必要的。对于其重要的项目来说,维护是我们工作的一部分。
三、关于编写 Clean Code 的总结
这篇文章到此就要结束了。
我们今天讨论的这六种做法可能不是那些影响最大或效果最显着的做法。但是,他们是经验丰富的开发人员最常提到的。这就是我选择他们的原因。我希望这些实践或技巧足以帮助你开始编写干净的代码。
现在,像所有事情一样,最重要的是开始。所以,选择至少一个练习,并尝试一下。
【本文为51CTO专栏作者“张旸”的原创稿件,转载请通过微信公众号联系作者获取授权】