设计模式学习(4)

148 阅读10分钟

评价代码质量

从哪些角度评价代码质量好坏?如何培养自己学高质量代码的能力。

具体问到什么是烂、好时,比如,好代码是易扩展、易读、简单、易维护等,但对于这些评价的理解往往只停留在表面概念上,对于更深入的问题,比如,“怎么才算可读性好?什么样的代码才算易扩展、易维护?可读、可扩展与可维护之间有什么关系?可维护中‘维护’两字该如何理解?“认识不足。

写出好代码的前提是能辨别出代码的好坏。如果连什么是好代码、什么是烂代码,都分辨不清,又谈何写出好代码呢?

日常描述代码质量的词汇:

灵活性(flexibility)、可扩展性(extensibility)、可维护性(maintainability)、可读性(readability)、易修改性(changeability)、可复用(reusability)、可测试性(testability)、模块化(modularity)、高内聚低耦合(high cohesion loose coupling)、高效(high effciency)、高性能(high performance)、安全性(security)、兼容性(compatibility)、易用性(usability)、整洁(clean)、清晰(clarity)、简单(simple)、直接(straightforward)、少即是多(less code is more)、文档详尽(well-documented)、分层清晰(well-layered)、正确性(correctness、bug free)、健壮性(robustness)、鲁棒性(robustness)、可用性(reliability)、可伸缩性(scalability)、稳定性(stability)、优雅(elegant)、好(good)、坏(bad)

很难通过其中的某个或者某几个词汇来全面地评价代码质量。因为这些词汇都是从不同维度来说的

代码质量高低也是一个综合各种因素得到的结论。并不能通过单一的维度去评价一段代码写的好坏。比如,即使一段代码的可扩展性很好,但可读性很差,那也不能说这段代码质量高。

除此之外,不同的评价维度也并不是完全独立的,有些是具有包含关系、重叠关系或者可以互相影响的。比如,代码的可读性好、可扩展性好,就意味着代码的可维护性好。 而且,各种评价维度也不是非黑即白的。比如,不能简单地将代码分为可读与不可读。如果用数字来量化代码的可读性的话,它应该是一个连续的区间值,而非 0、1 这样的离散值。

正是因为代码质量评价的主观性,越是有经验的工程师,给出的评价也就越准确。相反,资历比较浅的工程师就常常会觉得,没有一个可执行的客观的评价标准作为参考,很难准确地判断一段代码写得好与坏。有的时候,自己觉得代码写得已经够好了,但实际上并不是。所以,这也导致如果没有人指导的话,自己一个人闷头写代码,即便写再多的代码,代码能力也可能一直没有太大提高

笼统、抽象、比较偏向对于整体的描述,比如优雅、好、坏、整洁、清晰等.

过于细节、偏重方法论,比如模块化、高内聚低耦合、文档详尽、分层清晰等

不局限于编码,跟架构设计等也有关系,比如可伸缩性、可用性、稳定性等。

几个最常用的、最重要的评价标准,

  • 可维护性
  • 可读性
  • 可扩展性
  • 灵活性
  • 简洁性(简单、复杂)
  • 可复用性
  • 可测试性

可维护性(maintainability)

“维护”就是修改 bug、老的代码、添加新的代码之类的工作。

“代码易维护”就是指,在不破坏原有代码设计、不引入新的 bug 的情况下,能够快速地修改或者添加代码。

“代码不易维护”就是指,修改或者添加代码需要冒着极大的引入新 bug 的风险,并且需要花费很长的时间才能完成。

如何来判断代码可维护性的好坏?

实际上,可维护性也是一个很难量化、偏向对代码整体的评价标准,它有点笼统的评价。代码的可维护性是由很多因素协同作用的结果。代码的可读性好、简洁、可扩展性好,就会使得代码易维护; 相反,就会使得代码不易维护。更细化地讲,如果代码分层清晰、模块化好、高内聚低耦合、遵从基于接口而非实现编程的设计原则等等,那就可能意味着代码易维护。除此之外,代码的易维护性还跟项目代码量的多少、业务的复杂程度、利用到的技术的复杂程度、文档是否全面、团队成员的开发水平等诸多因素有关。

一个比较主观但又比较准确的感受。如果 bug 容易修复,修改、添加功能能够轻松完成,那就可以主观地认为代码易维护。相反,如果修改一个 bug,修改、添加一个功能,需要花费很长的时间,那就可以主观地认为代码不易维护。

是否易维护本来就是针对维护的人来说的。不同水平的人对于同一份代码的维护能力并不是相同的。对于同样一个系统,熟悉它的资深工程师会觉得代码的可维护性还不错,而一些新人因为不熟悉代码,修改 bug、修改添加代码要花费很长的时间,就有可能会觉得代码的可维护性不那么好。代码质量的评价有很强的主观性。

可读性(readability)

在编写代码的时候,时刻要考虑到代码是否易读、易理解。除此之外,代码的可读性在非常大程度上会影响代码的可维护性。毕竟,不管是修改 bug,还是修改添加功能代码,首先要做的事情就是读懂代码。代码读不大懂,就很有可能因为考虑不周全,而引入新的 bug。(针对复杂逻辑的代码,自己需要注意多写注释。)

如何来判断代码可读性?需要看代码是否符合编码规范、命名是否达意、注释是否详尽、函数是否长短合适、模块划分是否清晰、是否符合高内聚低耦合等。

实际上,code review 是一个很好的测验代码可读性的手段。如果你的同事可以轻松地读懂你写的代码,那说明你的代码可读性很好;如果同事在读你的代码时,有很多疑问,那就说明你的代码可读性有待提高。

可扩展性(extensibility)

可扩展性表示代码应对未来需求变化的能力。跟可读性一样,代码是否易扩展也很大程度上决定代码是否易维护。那什么是代码的可扩展性?

代码的可扩展性表示,在不修改或少量修改原有代码的情况下,通过扩展的方式添加新的功能代码。说直白点就是,代码预留了一些功能扩展点,你可以把新功能代码,直接插到扩展点上,而不需要因为要添加一个功能而大动干戈,改动大量的原始代码。

原则——“对修改关闭,对扩展开放”。

有的时候为了提高代码的可扩展性和可复用性 就会抽象出好多的接口,类和方法。 然后代码的简洁性和可读性就降低,扩展性和可读性有的时候是相冲突的。

扩展主要是指添加功能,维护更广些,添加、修改、可读性和可扩展性都影响到代码的可维护性。

灵活性(flexibility)

什么情况下才会说代码写得好灵活呢?这里罗列了几个场景:

  • 当添加一个新的功能代码的时候,原有的代码已经预留好了扩展点,不需要修改原有的代码,只要在扩展点上添加新的代码即可。这个时候,除了可以说代码易扩展,还可以说代码写得灵活。
  • 当要实现一个功能的时候,发现原有代码中,已经抽象出了很多底层可以复用的模块、类等代码,可以拿来直接使用。这个时候,除了可以说代码易复用之外,还可以说代码写得灵活。
  • 当使用某组接口的时候,如果这组接口可以应对各种使用场景,满足各种不同的需求,除了可以说接口易用之外,还可以说这个接口设计得好灵活或者代码写得灵活。

从刚举的场景来看,如果一段代码易扩展、易复用或者易用,都可以称这段代码写得比较灵活。所以,灵活这个词的含义非常宽泛,很多场景下都可以使用。

简洁性(simplicity)

设计原则——KISS 原则:“Keep It Simple,Stupid”。

尽量保持代码简单、逻辑清晰,也就意味着易读、易维护。在编写代码的时候,往往也会把简单、清晰放到首位。

很多编程经验不足的程序员会觉得,简单的代码没有技术含量,喜欢在项目中引入一些复杂的设计模式,体现自己的技术水平。实际上,思从深而行从简,真正的高手能云淡风轻地用最简单的方法解决最复杂的问题。这也是一个编程老手跟编程新手的本质区别之一。

可复用性(reusability)

尽量减少重复代码的编写,复用已有的代码。

面向对象的继承、多态存在的目的之一,就是为了提高代码的可复用性;当讲到设计原则的时候,会讲到单一职责原则也跟代码的可复用性相关;当讲到重构技巧的时候,会讲到解耦、高内聚、模块化等都能提高代码的可复用性。可复用性是很多设计原则、思想、模式等所要达到的最终效果。

DRY(Don’t Repeat Yourself),有哪些编程方法可以提高代码的复用性。

可测试性(testability)

代码可测试性的好坏,能从侧面上非常准确地反应代码质量的好坏。代码的可测试性差,比较难写单元测试,那基本上就能说明代码设计得有问题。

错误处理

还要关注代码的错误处理。

如何培养编码能力

如何写出易维护、易读、易扩展、灵活、简洁、可复用、可测试的代码?需要掌握编程方法论,包括面向对象设计思想、设计原则、设计模式、编码规范、重构技巧等。而所有这些编程方法论的最终目的都是为了编写出高质量的代码。

比如,面向对象中的继承、多态能让我们写出可复用的代码;编码规范能让我们写出可读性好的代码;设计原则中的单一职责、DRY、基于接口而非实现、里式替换原则等,可以写出可复用、灵活、可读性好、易扩展、易维护的代码;设计模式可以让我们写出易扩展的代码;持续重构可以时刻保持代码的可维护性等。