zoukankan      html  css  js  c++  java
  • 《编程匠艺》之开发工具与技巧

    第二部分: 代码的神秘生命(代码开发的技术与技巧)

    1. 欲善其事,先利其器(使用工具)

    1. 尽可能全面的了解你的常用工具, 为此投入的时间是值得的.
    2. 使用工具发挥作用:
      • 了解它能做什么
      • 学习如何驾驭它
      • 了解它适合什么任务
      • 检查它是否可用
      • 找到了解更多信息的途径
    3. 工具链的组成
      1. 源代码编辑工具
        • 代码编辑器
        • 代码处理工具(diff比较工具, sed流查找修改工具, awk样式匹配工具, grep正则匹配工具, find/locate文件查找工具)
        • 代码浏览工具
        • 版本控制工具
        • 源代码生成工具
        • 源代码美化工具
      2. 代码构建工具
        • 编译器(优化到什么程度?编译器遵循的标准是什么?如何生成正确指令集的代码)
        • 链接器(是否可以生成库对象, 静态库,动态库;是否能生成可调试文件)
        • 构建环境(make, automake, cmake等)
        • 测试工具链(如自动化单元测试框架, 模拟内存不足, 负载过高等)
      3. 调试和调查工具
        • 调试器(gdb, ddd)
        • 分析器(性能分析等)
        • 代码校验器(静态检查如lint, 动态工具如内存分配/边界检查器)
        • 度量工具(统计函数,注/代 比, 圈复杂度, 重复度等)
        • 反汇编工具
        • 缺陷跟踪系统
      4. 语言支持工具
        • 编程语言本身(语言特性,如go协程)
        • 运行时和解释程序(如java虚拟机)
        • 组件和库()
      5. 其他工具
        • 文档工具
        • 项目管理
    4. 工具总结
      • 你的工具箱中有哪些工具?哪些是频繁使用的?
      • 是否已经发挥了工具的最大作用?是否尝试优化使用?
      • 是否已经是能找到的最好工具?

    2. 测试代码的魔术(保证代码质量)

    1. 测试分为不同的层次,作为开发人员,主要关注的是在代码实现过程中的测试.

    2. 关于测试的疑问:

      • 为什么需要测试?这个应该不需要再复述了,为了保证交付的软件是满足要求的.
      • 谁来测试?自己应该为自己写的代码进行充分的测试,而不是寄希望与测试人员.
      • 测试内容都有哪些?这是个很好的问题,需要测试代码中的函数,类,流程等,根据测试的内容不同,可以分为单元测试和功能测试与集成测试.
      • 何时开始测试?越早开始成本越低.
    3. 影响测试难度的因素:

      • 分支与条件多少
      • 代码规模
      • 依赖关系
      • 外部输入
      • 多线程
      • 代码演变
    4. 测试类型

      • 单元测试(测试类或者函数)
      • 组件测试(验证一个或者多个单元组成的完整组件行为)
      • 集成测试(测试下一个系统中的多个组件,确保正确相连)
      • 回归测试
      • 负载测试(确保性能达到)
      • 压力测试(确保超负荷时,不会乱成一团,用于获取软件的实际容量)
      • 疲劳测试(使代码在较高负荷下连续工作一段时间,以确定是否有内存泄露和内存碎片化造成的性能降低)
      • 可用性测试(用户使用测试)
    5. 挑选单元测试用例

      • 既然不能测试所有的情况,那么怎么挑选用例呢?可以挑选下面的主要点:
        1. 良好的输入
        2. 不好的输入(如极大值,极小值, 过长或者过短字符串, 空字符串)
        3. 边界值(边界值本身,边界值上方, 边界值下方)
        4. 随机数(自动化随机产生)
        5. 零值
    6. 编写可测试的代码

      • 使各部分代码自包含,尽量减少不必要的关联
      • 不要依赖于全局变量
      • 限制代码的复杂度,拆分代码.
      • 保持代码的可观测性.
    7. 测试自动化

      • 将自动化运行的单元测试成为你构建的一部分
      • 测试的代码应尽量逻辑简单,避免测试代码出现问题
    8. 故障描述

      • 在测试出现问题时,需要详细的描述问题,描述的内容包括:
        • 出现问题时的环境(软件版本,硬件版本)
        • 可以使问题复现的最简单的步骤
        • 关于问题出现的可重复性和频率
        • 有可能相关联的其他事物
    9. 在测试时出现的问题就应该进行跟踪,并对其进行自动化测试的覆盖.

    10. 开发测试管理

      • 缺陷跟踪系统(报告故障, 分配责任, 确定优先级, 标记状态)
      • bug审查(重点在于讨论缺陷以及如何处理, 不要讨论修改细节)
    11. 单元测试需要到什么程度?如果一段代码简单的看一下已经不能证明是否正确的时候,就该引入测试用例了.

    12. 测试驱动开发的模式, 编写代码之前的测试只能是黑盒测试.

    3. 寻找缺陷, 并解决它(如何解决问题)

    1. bug的种类(如果你能准确知道它, 就能控制它)
      • 从远处看(三类)
      1. 编译失败
      2. 运行时崩溃
      3. 非预期的输出行为
      • 从近处看(更细致的分类)
      1. 句法错误(避免的方式是打开所有编译告警,并使代码通过lint检查)
      2. 构建错误(彻底清除中间构建, 从头构建)
      3. 语义错误(变量未初始化, 比较浮点数, 数值溢出, 隐士类型转换; 使用lint检查)
      • 从更近处看(语义缺陷)
      1. 段错误(主要是错误的指针使用)
      2. 内存溢出(表现可能是运行很远处出现莫名的错误)
      3. 内存泄露()
      4. 内存耗尽
      5. 数学错误(浮点异常, 溢出, 除数为0等)
      6. 程序暂停(无限循环, 死锁, 竞争)
    2. 除错的艺术
      • 地下之路
      • 地上之路
    3. 调试工具
      • 调试器(如gdb)
      • 内存访问校验器(确认是否有内存泄露和溢出)
      • 系统调用追踪(如strace)
      • 内核转储core文件
      • 日志
      • 静态分析器
    4. 调试箴言
      • 避免使用调试器"闲逛", 要注意调试黄金法则:多动脑子.

    4. 代码构建

    1. 主要的构建机制有三种:
      • 解释型语言
      • 编译型语言
      • 字节型语言
    2. 同一项目中,所有的成员都应该使用相同的构建系统,否则,构建的就不是同一个软件,可能存在参数等差异.
    3. 构建应该注意的事项:
      • 构建完成后,需要为发行打包
      • 每个版本都需要存档存储,或者在git上打一个版本tag.
      • 每个版本都需要一个发行说明
      • 当构建版本时,必须选择正确的编译器开关集.

    5. 优化代码(追求速度和效率)

    1. 软件优化的含义:

      • 程序的执行速度加快
      • 减小可执行文件的大小
      • 提高代码的质量
      • 提高计算结果的准确性
      • 将启动时间减到最小
      • 增加数据的吞吐量
      • 减少存储开销
    2. 造成代码臃肿,运行慢,体积大的原因?

      • 不必要的复杂性
      • 间接(额外的中间层)
      • 重复(重复的调用复杂的计算过程)
      • 糟糕的设计(加大了沟通的模块的距离)
      • I/O等待
    3. 为什么不进行优化?

      • balabala...备选方案
    4. 为什么要进行优化?

      • balabala...特殊领域如游戏, dsp, 实时系统, 金融计算等领域.
    5. 怎么进行优化?

      • 确定程度运行的慢, 并证明确实需要优化(但是要注意,也许并不是慢在代码级,而是设计上有问题)
      • 找出运行最慢的代码,以这段代码为目标(使用合适的分析工具)
      • 先测试这段的性能
      • 对这段代码优化
      • 测试优化后的代码是否功能正常
      • 测试速度提升多少,并决定下一步
    6. 怎么分析哪段代码最慢?

      • 使用合适的分析工具
      • 手动添加计时
      • 计算每个函数的调用频率(有工具, 也可以利用编译器的hook)
      • 通过单个函数变慢来测试它对整个程序执行时间的影响
      • 在进行分析时,要谨慎的选择分析数据,可以选择基本的数据集, 高负荷的数据集和普通的数据集.
    7. 优化的技术

      • 优化有两种大的方向:修改设计和修改代码
      • 基于运行速度的优化包括:
        • 加快较慢代码的速度
        • 尽量少做较慢的事情
        • 将较慢的事情推迟到不得不进行的时候
      • 代码设计层的修改包括:
        • 添加缓存层, 加快较慢的数据的访问
        • 使用资源池
        • 为速度牺牲一点精度
        • 变串行为并行,使用多线程模型
        • 使用更合适的算法和数据结构
      • 代码层的修改包括:
        • 编译器的优化级别提高
        • 循环展开
        • 代码内嵌(inline)
        • 移到编译时(如通过设置uint, 省掉<0 的检查)
        • 强度折减(使用等价的操作替代其他指令, 如使用移位代替除法)
        • 子表达式(对于多个地方会用到的同一个操作,抽出来)
        • 无用代码删除
        • (下面的方式更推荐)
        • 如果发现一个函数慢, 那么不要频繁调用, 缓存结果
        • 跨语言封装, 比如把java重新在c中实现
        • 重新整理代码(推迟工作, 对函数做检查及时跳出, 循环条件中不做计算)
        • 空间换时间, 提前缓存需要大量计算的结果
        • 利用短路判断,把可能导致退出的条件放在前面
    8. 对程序性能产生深刻影响的决策有:

      • 功能数量 VS 代码规模
      • 程序速度 vs 内存消耗
      • 存储和缓存 vs 按需计算
      • 近似的计算 vs 精确的计算
      • 内嵌 vs 函数调用; 单一的 vs 模块化的
      • 通过引用或者地址传递 vs 传递副本
      • 通过硬件实现 vs 通过软件
      • 写死的直接访问 vs 间接访问
      • 预先确定的固定的值 vs 可变可配置的值
      • 编译时工作 vs 运行时工作
      • 本地函数调用 vs 远程函数调用
      • 巧妙的算法 vs 清晰的算法
    9. 较慢的程序的瓶颈可能在哪?

      • 内存颠簸(不停的换出)
      • 等待磁盘/网口等访问(等待I/O慢)
      • 等待较慢的数据库事务
      • 存在锁等待
    10. 对使用的语言和操作系统, 要大致了解其相关成本, 如函数调用, inline函数, 可以通过查看对应的指令, 来了解大致时间级别.

    6. 安全的代码(防止被黑)

    1. 做安全防护的第一步是:了解你拥有哪些重要的资源, 是否拥有一些敏感的信息或者特定的能力.

    2. 不安全的软件源头:

      • 不安全的设计和体系
      • 缓冲区溢出
      • 嵌入的查询字符串
      • 竞争状况(常出现在复杂的多现场模型里)
      • 整数溢出
    3. 编码中的保护方法

      • 限制设计中的输入数量,安排所有的通信通过系统某部分进行
      • 在尽可能低的权限上运行程序
      • 避免开发并不真正需要的功能
      • 不要依赖于不可靠的库
      • 避免存储敏感数据
      • 要对输入进行检查(包括命令行参数, 环境变量, web表单, 文件大小等);要检查输入的大小, 格式, 有效性以及数据的真正内容.
  • 相关阅读:
    sql2012,返回数据多时不走索引
    JeeSite 工作流Activiti的应用实例
    A011 Activiti工作流程开发的一些统一规则和实现原理(完整版)
    mstsc远程桌面全频或自定义窗口
    ECLIPSE 取消自动更新
    js中设置元素class的三种方法小结
    python笔记28-lxml.etree爬取html内容
    python笔记27-lxml.etree解析html
    Locust性能测试3-no-web模式和csv报告保存
    Locust性能测试2-先登录场景案例
  • 原文地址:https://www.cnblogs.com/yhp-smarthome/p/11074575.html
Copyright © 2011-2022 走看看