本着什么原则,才能写出优秀的代码? (一)

简介: 本着什么原则,才能写出优秀的代码? (一)

原文链接:本着什么原则,才能写出优秀的代码?


作为一名程序员,最不爱干的事情,除了开会之外,可能就是看别人的代码。


有的时候,新接手一个项目,打开代码一看,要不是身体好的话,可能直接气到晕厥。


风格各异,没有注释,甚至连最基本的格式缩进都做不到。这些代码存在的意义,可能就是为了证明一句话:又不是不能跑。


在这个时候,大部分程序员的想法是:这烂代码真是不想改,还不如直接重写。


但有的时候,我们看一些著名的开源项目时,又会感叹,代码写的真好,优雅。为什么好呢?又有点说不出来,总之就是好。


那么,这篇文章就试图分析一下好代码都有哪些特点,以及本着什么原则,才能写出优秀的代码。


初级阶段


先说说比较基本的原则,只要是程序员,不管是高级还是初级,都会考虑到的。


7326d6100e3a48679287325e17ed4c8c~tplv-k3u1fbpfcp-zoom-in-crop-mark 1304 0 0 0.png


这只是列举了一部分,还有很多,我挑选四项简单举例说明一下。


  1. 格式统一
  2. 命名规范
  3. 注释清晰
  4. 避免重复代码


以下用 Python 代码分别举例说明:


格式统一


格式统一包括很多方面,比如 import 语句,需要按照如下顺序编写:


  1. Python 标准库模块
  2. Python 第三方模块
  3. 应用程序自定义模块


然后每部分间用空行分隔。


import os
import sys
import msgpack
import zmq
import foo
复制代码


再比如,要添加适当的空格,像下面这段代码;


i=i+1
submitted +=1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
复制代码


代码都紧凑在一起了,很影响阅读。


i = i + 1
submitted += 1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
复制代码


添加空格之后,立刻感觉清晰了很多。


还有就是像 Python 的缩进,其他语言的大括号位置,是放在行尾,还是另起新行,都需要保证统一的风格。


有了统一的风格,会让代码看起来更加整洁。


命名规范


好的命名是不需要注释的,只要看一眼命名,就能知道变量或者函数的作用。


比如下面这段代码:


a = 'zhangsan'
b = 0
复制代码


a 可能还能猜到,但当代码量大的时候,如果满屏都是 abcd,那还不得原地爆炸。


把变量名稍微改一下,就会使语义更加清晰:


username = 'zhangsan'
count = 0
复制代码

还有就是命名要风格统一。如果用驼峰就都用驼峰,用下划线就都用下划线,不要有的用驼峰,有点用下划线,看起来非常分裂。


注释清晰


看别人代码的时候,最大的愿望就是注释清晰,但在自己写代码时,却从来不写。


但注释也不是越多越好,我总结了以下几点:


  1. 注释不限于中文或英文,但最好不要中英文混用
  2. 注释要言简意赅,一两句话把功能说清楚
  3. 能写文档注释应该尽量写文档注释
  4. 比较重要的代码段,可以用双等号分隔开,突出其重要性


举个例子:


# =====================================
# 非常重要的函数,一定谨慎使用 !!!
# =====================================
def func(arg1, arg2):
    """在这里写函数的一句话总结(如: 计算平均值).
    这里是具体描述.
    参数
    ----------
    arg1 : int
        arg1的具体描述
    arg2 : int
        arg2的具体描述
    返回值
    -------
    int
        返回值的具体描述
    参看
    --------
    otherfunc : 其它关联函数等...
    示例
    --------
    示例使用doctest格式, 在`>>>`后的代码可以被文档测试工具作为测试用例自动运行
    >>> a=[1,2,3]
    >>> print [x + 3 for x in a]
    [4, 5, 6]
    """
复制代码


避免重复代码


随着项目规模变大,开发人员增多,代码量肯定也会增加,避免不了的会出现很多重复代码,这些代码实现的功能是相同的。


虽然不影响项目运行,但重复代码的危害是很大的。最直接的影响就是,出现一个问题,要改很多处代码,一旦漏掉一处,就会引发 BUG。


比如下面这段代码:


import time
def funA():
    start = time.time()
    for i in range(1000000):
        pass
    end = time.time()
    print("funA cost time = %f s" % (end-start))
def funB():
    start = time.time()
    for i in range(2000000):
        pass
    end = time.time()
    print("funB cost time = %f s" % (end-start))
if __name__ == '__main__':
    funA()
    funB()
复制代码


funA()funB() 中都有输出函数运行时间的代码,那么就适合将这些重复代码抽象出来。


比如写一个装饰器:


def warps():
    def warp(func):
        def _warp(*args, **kwargs):
            start = time.time()
            func(*args, **kwargs)
            end = time.time()
            print("{} cost time = {}".format(getattr(func, '__name__'), (end-start)))
        return _warp
    return warp
复制代码


这样,通过装饰器方法,实现了同样的功能。以后如果需要修改的话,直接改装饰器就好了,一劳永逸。


进阶阶段


当代码写时间长了之后,肯定会对自己有更高的要求,而不只是格式注释这些基本规范。


但在这个过程中,也是有一些问题需要注意的,下面就来详细说说。


炫技


第一个要说的就是「炫技」,当对代码越来越熟悉之后,总想写一些高级用法。但现实造成的结果就是,往往会使代码过度设计。


这不得不说说我的亲身经历了,曾经有一段时间,我特别迷恋各种高级用法。


有一次写过一段很长的 SQL,而且很复杂,里面甚至还包含了一个递归调用。有「炫技」嫌疑的 Python 代码就更多了,往往就是一行代码包含了 N 多魔术方法。


然后在写完之后漏出满意的笑容,感慨自己技术真牛。


结果就是各种被骂,更重要的是,一个星期之后,自己都看不懂了。


b760bf5000d545b1977f0ea6c37cd4be~tplv-k3u1fbpfcp-zoom-in-crop-mark 1304 0 0 0.png

其实,代码并不是高级方法用的越多就越牛,而是要找到最适合的。


越简单的代码,越清晰的逻辑,就越不容易出错。而且在一个团队中,你的代码并不是你一个人维护,降低别人阅读,理解代码的成本也是很重要的。


脆弱


第二点需要关注的是代码的脆弱性,是否细微的改变就可能引起重大的故障。


代码里是不是充满了硬编码?如果是的话,则不是优雅的实现。很可能导致每次性能优化,或者配置变更就需要修改源代码。甚至还要重新打包,部署上线,非常麻烦。


而把这些硬编码提取出来,设计成可配置的,当需要变更时,直接改一下配置就可以了。


再来,对参数是不是有校验?或者容错处理?假如有一个 API 被第三方调用,如果第三方没按要求传参,会不会导致程序崩溃?


举个例子:


page = data['page']
size = data['size']
复制代码


这样的写法就没有下面的写法好:


page = data.get('page', 1)
size = data.get('size', 10)
复制代码


继续,项目中依赖的库是不是及时升级更新了?


积极,及时的升级可以避免跨大版本升级,因为跨大版本升级往往会带来很多问题。


还有就是在遇到一些安全漏洞时,升级是一个很好的解决办法。


最后一点,单元测试完善吗?覆盖率高吗?


说实话,程序员喜欢写代码,但往往不喜欢写单元测试,这是很不好的习惯。


有了完善,覆盖率高的单元测试,才能提高项目整体的健壮性,才能把因为修改代码带来的 BUG 的可能性降到最低。


重构


随着代码规模越来越大,重构是每一个开发人员都要面对的功课,Martin Fowler 将其定义为:在不改变软件外部行为的前提下,对其内部结构进行改变,使之更容易理解并便于修改。


重构的收益是明显的,可以提高代码质量和性能,并提高未来的开发效率。


但重构的风险也很大,如果没有理清代码逻辑,不能做好回归测试,那么重构势必会引发很多问题。


这就要求在开发过程中要特别注重代码质量。除了上文提到的一些规范之外,还要注意是不是滥用了面向对象编程原则,接口之间设计是不是过度耦合等一系列问题。


那么,在开发过程中,有没有一个指导性原则,可以用来规避这些问题呢?

当然是有的,接着往下看。


目录
相关文章
|
11月前
|
程序员
编程原则和模式
编程原则和模式
|
2月前
|
设计模式 算法 开发者
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
|
5月前
|
缓存 Java 编译器
什么是happens-before原则?
什么是happens-before原则?
85 0
|
12月前
|
测试技术
软件设计原则-单一置原则讲解以及代码示例
单一职责原则(Single Responsibility Principle,SRP)是面向对象设计中的一个重要原则,提倡将一个类或模块只负责一个职责或功能。它最早由Robert C. Martin在其《敏捷软件开发:原则、模式与实践》一书中提出。 单一职责原则的核心思想是:一个类或模块应该只有一个引起它变化的原因。也就是说,每个类或模块都应该只有一个职责或功能,并且该职责或功能应该在该类或模块内部封装起来,而不是分散到多个类或模块中。
72 0
|
12月前
软件设计原则-合成复用原则讲解以及代码示例
合成复用原则(Composition/Aggregation Reuse Principle,CARP)是面向对象设计的一种重要原则,也被称为组合/聚合复用原则。它强调通过组合(Composition)或聚合(Aggregation)关系来达到代码复用的目的,而不是通过继承关系。
172 0
|
设计模式 测试技术 程序员
代码的简单设计五原则
代码的简单设计五原则
33071 1
|
设计模式 Java 程序员
代码设计原则
代码设计原则
377 0
代码设计原则
|
设计模式 前端开发 关系型数据库
本着什么原则,才能写出优秀的代码? (二)
本着什么原则,才能写出优秀的代码? (二)
201 0
本着什么原则,才能写出优秀的代码? (二)
|
消息中间件 SQL 缓存
程序命名的原则与重构
命名是对事物本质的一种认知探索,是给读者一份宝贵的承诺。糟糕的命名会像迷雾,引领读者走进深渊;而好的命名会像灯塔,照亮读者前进的路。命名如此美妙,本文将一步步揭开它的神秘面纱!
程序命名的原则与重构
|
算法 关系型数据库 数据库
面向对象基本原则(3)- 最少知道原则与开闭原则
面向对象基本原则(1)- 单一职责原则与接口隔离原则 面向对象基本原则(2)- 里式代换原则与依赖倒置原则 面向对象基本原则(3)- 最少知道原则与开闭原则