美文网首页
第2章 编写高质量代码

第2章 编写高质量代码

作者: 躲到被窝头吹风 | 来源:发表于2019-02-01 09:54 被阅读0次

    第2章 编写高质量代码

    2.1 编写过程与规范

    2.1.1 软件编程工作

    系统模型 => 编写代码(代码审查和单元测试) -> 代码优化和系统构建 -> 集成调试 => 源代码 => 协作迭代……
    高质量软件开发之道 => 高质量的设置<>规范的编码<>有效的测试
    目的:提高编码质量,必变不必要的错误;增强程序代码的可读性,可重用性和可移植性
    编程规范:谷歌编程规范,阿里编程规范

    • 注释
      • 好的注释是解释为什么,而不是怎么样
      • 不要在注释中重复描述代码
      • 注释在精不在多
      • 维护代码的同时也应维护注释
    • 名称
      • 见名知意
      • 规范统一
    • 语句
      • 统一规范
      • 一行不能太长,过长的语句应换行
      • 一行应只有一条语句
      • 使用空行区分代码块
      • 缩进一致

    2.2 良好的编程实践

    看/问/练 永远是最好的实践方式

    2.1.1 软件开发的工程思维

    高楼大厦可能是这样建成的

    1. 分析问题
    2. 初步设计
    3. 模型评估与测试
    4. 搭建框架
    5. 建造
    6. 测试与验收

    分析过程:

    • 问题 -> 拆分 子问题

    合成过程:

    • 各个方案 -> 解决方案

    高质量的设计

    • 模块化设计
    • 面向抽象编程
    • 错误与异常处理

    2.1.2 模块化设计

    目的和好处:降低系统构造的复杂性,增加灵活性;思路清晰,易于测试,适应未来可能的变化。

    如:活字印刷术,微服务都降低了工程的复杂程度,提升了可复用的程度。

    2.1.2.1 基本思想

    将一个大的程序按功能拆分成一系列小的模块,各个模块相对独立、功能单一、结构清晰、接口简单,模块之间通过接口进行调用。

    • 降低程序设计的复杂性
    • 提高模块的可靠性和复用性
    • 缩短开发周期
    • 易于维护和功能拓展
    WEB应用模块划分示例.PNG

    2.1.2.2 基于易变与稳定

    认识和识别变与不变的部分,并将之科学的分离开。

    模块化设计-基于易变与稳定.PNG

    2.1.2.3 基于单一职责

    类或函数应该只做一件事,并且做好这件事。

    • 单一职责:不等于单一功能
    • 单一职责:只有一个引起变化的原因
    • 原则:避免编写万能类和庞大的函数

    2.1.3 面向抽象编程

    在模块化设计的基础上,我们可以先设计出各个模块的“骨架”,或者说对各个模块进行“抽象”,定义它们之间的“接口”。
    定义各个模块互相关联的部分,这些部分在未来开发中不应该发生改变。

    2.1.4 错误与异常处理

    错误是导致程序崩溃的问题,例如Python程序的语法错误(解析错误)或者未捕获的异常(运行错误)等。

    异常是运行时期检测到的错误,即使一条语句或者表达式在语法上是正确的,当试图执行它时也可能会引发错误。

    • 捕获异常时try的代码块应当尽量精确,避免捕获意料外的异常
    • 细化异常类型,对异常进行分类集中处理
    • 在关键部分应检查变量的合法性,如取值范围等,避免“雪球效应”
    • 给用户一个友好的错误提示

    2.3 Python集成开发环境

    PyCharm

    2.4 代码静态检查

    2.4.1 代码评审技术

    代码审查(Code Review)是一种用来确认方案设计和代码实现的质量保证机制,它通过阅读代码来检查源代码与编码规范的符合性以及代码的质量。

    代码审查的作用

    检查设计的合理性

    • 互为 Backup
    • 分享知识、设计、技术
    • 增加代码可读性
    • 处理代码中的“地雷区”

    2.4.2 缺陷检查表

    类别 说明
    编程规范 按照具体编程语言的编码规范进行检查,包括命名规则、程序注释、缩进排版、声明与初始化、语句格式等。
    面向对象设计 • 类的设计和抽象是否合适
    • 是否符合面向接口编程的思想<br />• 是否使用合适的设计模式
    性能方面 • 在出现海量数据时,队列、表、文件在传输、上载等方面是否会出现问题,是否控制如分配的内存块大小、队列长度等<br />• 对 Hashtable、Vector 等集合类数据结构的选择和设置是否合适<br />• 有无滥用 String 对象的现象<br />• 是否采用通用的线程池、对象池等高速缓存技术以提高性能<br />• 类的接口是否定义良好,如参数类型等应避免内部转换<br />• 是否采用内存或硬盘缓冲机制以提高效率?
    • 并发访问时的应对策略<br />• I/O 方面是否使用了合适的类或采用良好的方法以提高性能(如减少序列化、使用 buffer 类封装流等)<br />• 同步方法的使用是否得当,是否过度使用?<br />• 递归方法中的迭代次数是否合适(应保证在合理的栈空间范围内)<br />• 如果调用了阻塞方法,是否考虑了保证性能的措施<br />• 避免过度优化,对性能要求高的代码是否使用profile工具
    资源释放处理 • 分配的内存是否释放,尤其在错误处理路径上(如 C/C++)
    • 错误发生时是否所有对象被释放,如数据库连接、Socket、文件等<br />• 是否同一个对象被释放多次(如 C/C++)<br />• 代码是否保存准确的对象引用计数
    程序流程 • 循环结束条件是否准确
    • 是否避免了死循环的产生<br />• 对循环的处理是否合适,应考虑到性能方面的影响
    线程安全 • 代码中所有的全局变量是否是线程安全的
    • 需要被多个线程访问的对象是否线程安全,检查有无通过同步方法保护<br />• 同步对象上的锁是否按相同的顺序获得和释放以避免死锁,注意错误处理代码<br />• 是否存在可能的死锁或是竞争,当用到多个锁时,避免出现类似情况:线程A获得锁1,然后锁2,线程B获得锁2,然后锁1<br />• 在保证线程安全的同时,注意避免过度使用同步,导致性能降低
    数据库处理 • 数据库设计或SQL语句是否便于移植(注意与性能会存在冲突)
    • 数据库资源是否正常关闭和释放<br />• 数据库访问模块是否正确封装,便于管理和提高性能
    • 是否采用合适的事务隔离级别<br />• 是否采用存储过程以提高性能<br />• 是否采用 PreparedStatement 以提高性能
    通讯方面 • Socket 通讯是否存在长期阻塞问题
    • 发送接收的数据流是否采用缓冲机制• Socket 超时处理和异常处理• 数据传输的流量控制问题
    JAVA对象处理 • 对象生命周期的处理,是否对象引用已失效可设置 null 并被回收
    • 在对象传值和传参方面有无问题,对象的 clone 方法使用是否过度• 是否大量经常地创建临时对象• 是否尽量使用局部对象(堆栈对象)• 在只需要对象引用的地方是否创建了新的对象实例
    异常处理 • 每次当方法返回时是否正确处理了异常,如最简单的处理是记录日志到日志文件中<br />• 是否对数据的值和范围是否合法进行校验,包括使用断言<br />• 在出错路径上是否所有的资源和内存都已经释放<br />• 所有抛出的异常是否都得到正确的处理,特别是对子方法抛出的异常,在整个调用栈中必须能够被捕捉并处理<br />• 当调用导致错误发生时,方法的调用者应该得到一个通知<br />• 不要忘了对错误处理部分的代码进行测试,很多代码在正常情况下执行良好,而一旦出错整个系统就崩溃了?
    方法(函数) • 方法的参数是否都做了校验
    • 数组类结构是否做了边界校验<br />• 变量在使用前是否做了初始化
    • 返回堆对象的引用,不要返回栈对象的引用<br />• 方法的 API 是否被良好定义,即是否尽量面向接口编程,以便于维护和重构
    安全方面 • 对命令行执行的代码,需要详细检查命令行参数
    • WEB 类程序检查是否对访问参数进行合法性验证<br />• 重要信息的保存是否选用合适的加密算法<br />• 通讯时考虑是否选用安全的通讯方式
    其他 日志是否正常输出和控制
    • 配置信息如何获得,是否有硬编码

    2.5 代码性能分析

    2.5.1 代码性能优化

    2.5.1.1 优化思想

    优化是对代码进行等价变换,使得变换后的代码运行结果与变换前的代码运行结果相同,但执行速度加快或存储开销减少。

    程序性能:空间复杂性<>时间复杂性

    2.5.1.2 优化方面

    • 代码性能优化是一门复杂的学问。
    • 根据 80/20 原则,实现程序的重构、优化、扩展以及文档相关的事情通常需要消耗80% 的工作量。
    • 在满足正确性、可靠性、健壮性、可读性等质量因素的前提下,设法提高程序的效率。
    • 以提高程序的全局效率为主,提高局部效率为辅。
    • 在优化程序效率时,应先找出限制效率的“瓶颈”。
    • 先优化数据结构和算法,再优化执行代码。
    • 时间效率和空间效率可能是对立的,应当分析哪一个因素更重要,再做出适当的折衷。
    • 从一开始就要考虑程序性能,不要期待在开发结束后再做一些快速调整。
    • 正确的代码要比速度快的代码重要,任何优化都不能破坏代码的正确性。
      1. 证明需要进行优化
      2. 找出优化关键部分
      3. 测试代码
      4. 优化代码
      5. 评测优化后的代码
    • 认真选择测试数据,使其能够代表实际的使用状况。
    • 永远不要在没有执行前后性能评估的情况下尝试对代码进行优化。

    2.5.2 代码性能优化

    2.5.2.1 改进算法,选择合适的数据结构

    • 良好的算法对性能起到关键作用,因此性能改进的首要点是对算法改进
    • 算法时间复杂性的排序依次是
      • O(1) è O(lg n) è O(n lg n) è O(n^2) è O(n^3) è O(n^k) è O(k^n) è O(n!)
    • 对成员的查找访问等操作,字典(dictionary)要比列表(list)更快
    • 集合(set)的并、交、差的操作比列表(list)的迭代要快

    2.5.2.2 循环优化的基本原则

    尽量减少循环过程中的计算量,在多重循环的时候,尽量将内层的计算提到上一层。

    2.5.2.3 字符串的优化

    Python的字符串对象是不可改变的。字符串连接的使用尽量使用 join() 而不是 +。当对字符串可以使用正则表达式或者内置函数处理时,选择内置函数。

    2.5.2.4 使用列表解析和生成器表达式

    列表解析要比在循环中重新构建一个新的list 更为高效,因此可以利用这一特性来提高运行的效率。

    相关文章

      网友评论

          本文标题:第2章 编写高质量代码

          本文链接:https://www.haomeiwen.com/subject/xwbrsqtx.html