编程匠艺
1章:善于防守:健壮代码的防御性编程技巧 不要做任何假设 防御性编程技巧:好的代码风格和合理设计;不要仓促编写;不要相信任何输入;编码的目标是清晰而不是简洁;启用所有警告;静态分析工具lint;安全数据结构;检查所有返回值;审慎处理内存;
2章:精心布局:源代码的版面和样式 一致,传统,简洁
3章:名正言顺:起名 身份;行为;识别 描述性;技术上正确;符合语言习惯;恰当(长度清晰而非简洁) 变量:(名词或者动词行名词) 函数:(至少包含一个动词)
4章:不言自明:编写自文档化代码的技巧 好的样式编写简单的代码;有意义的名称;分解为原子函数;确保重要代码突出;分组相关信息;恰当处理错误;有意义的注释;
5章:随篇注释:如何编写代码注释 解释为什么,而不是怎么样
6章:人非圣贤:处理不可避免的情况-代码中的错误情形 分类:用户错误、程序员错误、意外情况 错误处理:返回值;错误状态变量;异常(终止模式、恢复模式);信号; 处理:错误来自何处?当时你正要做什么?为什么会出错?错误何时发生?严重性;如何修正?何时处理错误? 常见反应:日志;报告;恢复;忽略; 检查:函数参数;关键点不变条件;检测外部值的有效性;检查系统调用返回状态
7章:使用工具构建软件
8章:测试时代,测试代码的魔术 测试只能证明存在缺陷,而不能证明不存在缺陷。 验证所有有效的输入,都会产生正确的输出;验证所有无效输出,都会产生适当的故障行为(但是测试集合太大,所以只能选典型)
良好的输入:不好的输入;边界值;随机数据;0;
为测试而设计代码:使各个代码部分自包含;不要依赖全局变量;限制代码复杂度;保证代码可观测性; 将自动运行单元测试作为你构建过程的一部分。
9章:寻找缺陷:调试 bug种类: - 从远处看:编译失败;运行时崩溃;非预期行为(越早修复,成本越低) - 从近处看:句法错误;构建错误;基本语义bug(浮点数直接比较,数值溢出,隐式转换);语义问题 - 更近处:段错误;内存溢出;内存泄露;内存耗尽;数学错误(浮点异常、不正确数学结构、溢出、除以0);程序暂停
寻找bug:多动脑筋,不要胡乱尝试; - 地下之路:不要胡乱再试一次,设定时间用完就使用更加系统的方法 - 地上之路:查找缺陷的难易在于熟悉程度,首先了解它;对执行环境的控制能力;不要信任任何人的代码
搜寻bug: - 编译时错误:从第一条编译错误开始看 - 运行时错误:观察现象;建立解释这种现象的假设;使用假设预测更多观察的结果;进行试验,检查预测。 确定故障;使故障再现;定位缺陷(分而治之,跟踪执行);理解问题(彻底研究证明你是正确的,而不是修复之后引入更多缺陷);创建测试(编写测试用例);修正缺陷;证明你修正了缺陷(把测试用例加入回归测试);
如何修正缺陷:不要在修改代码时破坏其他代码 这次修改的后果是什么?会不会影响调用这个函数的任何代码?是否会轻微改变代码的行为 是否相关代码存在相同错误,对所有地方进行修正。(复制粘贴很危险)吸取教训(怎样预防与快速发现)
预防:谨慎地防御性编程;优秀的编程就是遵循章法和注意细节; 调试器:有节制使用,不要不停地使用调试器而不去理解代码如何工作 内存访问校验器;系统调用错误(strace);内核转储;日志;静态分析器(lint)
10章:代码构建:源代码转化为可执行程序的过程 解释型语言;编译型语言;字节编译型语言;
11章:追求速度:优化程序和编写高效的代码 正确的代码远比速度快的代码重要; 来源:不必要的复杂性;间接(抽象OOP带来);重复;糟糕设计;I/O 为什么不进行优化?:可读性损失;复杂性增加;难以维护的扩展;引入冲突;更多的努力(可能会浪费精力);很多时候优化是不经济的,预先就注意编写高效代码
优化的具体细节: - 确定程序运行慢,证明确实需要优化 - 找出慢的代码,以此为目标 - 测试目标代码性能 - 对目标代码优化 - 测试优化后是否可以正常执行 - 测试速度增加了多少,下一步做什么
优化技术:设计更改和代码更改 基于速度的优化策略:加快慢代码执行速度;少做慢事;推迟慢操作(时间空间互换) 设计更改:宏观;缓冲层;资源库(进程池,连接池);牺牲一点精确性;更改数据存储模式;并行和线程;避免过度使用异常;益处不必要功能;牺牲一些设计质量(减少简介层、增加耦合);算法和数据结构优化
代码更改:编译器优化;循环展开;代码内嵌;移到编译时;强度折减(位运算);子表达式;删除无用代码;重新整理代码(延迟计算、早失败、不变条件计算移到循环外);复杂计算使用查找表(空间换时间);短路求值;不要重复相同工作(重复造轮子);
编写高效代码:不要以牺牲质量为代价,只有在性能确实是问题的情况下,再去考虑代码级别优化
12章:编码安全的程序 软件中的弱点 不安全的设计和体系结构:所有系统组建必须考虑安全性 缓冲溢出:未知子字符串长度 嵌入查询字符串:xss 竞争状况:依赖执行顺序的系统 数值溢出:
防范措施: 系统安装技术:受信任软件;防火墙;记录所有操纵;正确设置;安装诱饵蜜罐; 软件设计技术:限制输入数量;低权限运行程序;移除非必须功能;依赖安全第三方库;加密数据;
代码实现技术:防御性编程(不做假设);专家安全审核;严格测试和调试;;所有操作打包到原子事务中,攻击者无法利用竞争条件获利
13章:崇尚设计:如何创作出优秀的软件设计 编程本身就是一种设计活动; 软件设计层次: - 系统体系结构 - 模块、组件 - 类和数据类型 - 函数
良好的软件设计: - 反复(渐进式构建) - 谨慎:不要一次过多设计 - 现实:经验 - 信息充分: 简洁:扩展性vs简洁;效率vs安全;功能vs工作量; 优雅:类似地方关联在一起 模块化:强内聚;低耦合;分解的结果必须是对问题空间的有效划分 良好的接口:确定客户端,了解需求;确定供应端,了解它的能力;推断所需接口类型(函数、类、网络协议);确定操作的性质(划分,清晰定义角色和责任;抽象,隐藏什么,忽略什么;简练;可替代性;);可扩展性(但是不要过于宽泛);避免重复;可移植性;符合语言习惯;良好文档化
如何设计代码: 结构化设计:功能分解:自顶向下和自底向上 面向对象设计:对象(数据和操作) 设计工具:表示法;设计模式;流程图;伪代码;
14章:软件体系结构:奠定软件设计的基础 什么是软件体系结构: 软件蓝图:确定关键模块;确定各个组件如何通信;阐明各个系统的角色和职责 视图:概念视图;实现视图;进程视图;部署视图; 何时何处进行体系结构设计:高层次文档中 体系结构用来做什么:验证;沟通;判断优劣; 组件和连接: 什么是良好的体系:确定关键组建及其联系;易于理解和应用; 体系结构风格:分层体系结构;管道和过滤体系结构;客户端/服务器体系结构(接口:api,类层次,组件技术、数据格式);基于组件的体系结构;框架;
15章:改良与革命:代码如何成长 软件腐烂:总体开发的40%~80%要花在维护上。-Boehm 警告信号:了解代码腐化的新号 代码如何成长的:运气;增长;重写往往优于胡乱修补;重新调整 编写新代码:模块之间连接方式;模块化和信息隐藏;扩展性;整洁清晰的代码;KISS 维护现有代码:重要性排序;只进行必要更改;监控一次的更改量;要非常了解你要处理的代码(处于什么位置、依赖关系、创建之初的设想、已修改的历史纪录、正确的态度修补还是重写);不要在添加新代码的时候引入额外依赖关系(降低复杂性);保持编程风格;使用测试套件检查是否引入破坏;明白修正缺陷的真正原因;如果修改很糙,取消它;
16章:代码猴子:培养正确的编程态度和方法 各种各样的代码猴子:(优点,缺点,如果你是其中一员,如何与之相处) - 卖力工作的程序员:多产,喜欢新东西;性子急,糟糕的调试者;->编写单元测试 - 代码猴子:可靠;思维局限性强,不会主动调查和修正问题,对新事物没兴趣;教他们改善工作的技巧 - 权威:经验丰富;对话困难;不要期望每个人都像你一样敏捷;向他们学习 - 半权威:傲慢;警惕;小心打交道 - 傲慢的天才:技术领导力和催化剂;永远认为自己什么都知道;像更有阅历的人学习;学习不带情绪地与之讨论,不要让自己傲慢无礼 - 牛仔:懒惰,急于完成任务;代码可以运行但是不优雅;你将花费大量时间做清理工作;承认缺点尝试改变;对他们的代码审核、结对编程 - 规划者:深思熟虑;过度设计,懂得很多知识,但是没有升华为实践;规划与行动之间平衡;时间超前一点 - 老前辈:知道优秀代码与避免陷阱;不愿意学习新技术,抵制改变,没有耐心;不要苛求年轻人;表示尊重,但不要奉为神明 - 狂热者:对大公司技术熟悉,有效率;既不客观,也不实际,错过更好的非大公司的设计;不要有偏见;用充分的信息告诉他优缺点 - 单线条程序员:专心、努力;期望别人也像他一样专心投入,总会忽略一些事情;一张一弛;不要把所有时间花在项目上 - 拖踏者:知道如何找乐子;可能知识缺乏快速解决问题的技巧;要有职业精神;不要抱怨,用行动证明是拖踏着的错 - 勉强的团队领导:同情程序员的境遇;哪一件都做不好,不能恰当管理团队;接受培训;同情心,帮助领导 - 你:。。。
理想的程序员: - 政治家:应付管理、测试、支持、客户、用户等,老练 - 亲切:良好的沟通技巧,善于演讲、也善于聆听 - 艺术感:高质量的解决方案 - 技术天才:广泛的技术能力,明白如何以及何时使用他们
怎么办?做出改变 - 你最想哪种类型的代码猴子? - 你独特的优点和缺点是什么? - 看看可以做出哪些改变?
良好的态度: - 团队精神:了解每个同事的特点,学会更好地与之相处 - 诚实和谦虚:了解你的优点和缺点。主动帮助别人的态度 - 不断提高 - 体贴:时刻思考你在做什么?编写代码的时候,想想准备做什么?完成后看看都写了什么 - 热情:不断阅读与实践
17章:团结就是力量:团队合作与个人程序员 团队组织: - 管理方法:平级管理,地位相等 - 责任划分:垂直型全才团队;水平型专才;结对编程 组织和代码结构:康威定律(组织形式影响代码结构);围绕要构建的代码组织团队,不要围绕团队组织代码
合作工具:版本控制;缺陷数据库;协同工作系统;方法(规范等);计划;
团队疾病: - 巴别塔:缺乏交流,脱节;团建活动,结对编程 - 独裁制:重新建立团队结构; - 民主制:相似的技术水平和互补个性;没有人确保任务完成; - 卫星站:物理上分开;需要细致监控和管理 - 大峡谷:初级程序员和高级程序员的鸿沟,不健康的团队会发展出许多小圈子;改变座位布局;结对编程;师徒计划 - 流沙:团队有一名不能胜任工作的程序员;铲除流沙? - 旅鼠:只是代码雇佣兵,不会指出问题;不要鼠目寸光地修改;坚持良好的编程原则,不要相信未来有修补代码的机会
良好合作的个人技巧和特点: - 沟通:编写没有歧义的规范; - 谦虚:倾听并且珍惜别人的意见 - 处理冲突:论事不论人 - 学习和适应能力:充分利用个人技术让集体受益 - 了解你的不足:尽早提出以免带来麻烦
团队合作原则: - 集体代码所有制 - 尊重别人的代码:不要随意更改(改了加注释) - 编码准则:必须有一个意志的一件 - 定义成功:小规模庆祝 - 定义责任:职责与责任 - 避免倦怠:改变
团队生命周期: 创建:选择合作模式 内部团队成长:确定目标;规范; 外部团队成长:分析师、架构师、dba、设计师、程序员、项目经理、qa、用户培训师、运维、技术支持、 合作:合适的工作节奏 结束:没有团队成员可以永远持续下去;执行移交(所有代码文档、测试用具和维护指令)
经验: 人不在多而在精 任务与能力匹配,也与激励匹配; 为人才投资 不要培养专家:一旦离职无法维护 选择互补人员:健康人际关系组合 清楚障碍:清除不合适人员
18章:安全措施:源代码控制与自我控制 我们的责任:安全可靠;可访问;可复制;可维护; 小修改高频次签入代码
19章:注意细节:编写软件规范 规范重要的沟通机制:更加安全(信息丢失,人员离职);可访问(新人上手);更加准确(更可能发现问题和副作用) 规范的类型: 需求规范:代码预期怎么样的行为;功能需求,性能需求,操作性需求,未来的操作需求 功能规范:可观察到的行为 系统体系结构规范: 用户界面规范:看起来什么样以及如何反应。ui规范 设计规范:内部api、数据结构和格式,不要过时 测试规范:测试策略
唱反调的人 规范应当包含哪些内容:正确;可理解;完整;可验证;可修改;自描述;可追踪;
编写过程: - 何时,模板起步; - 编写 - 审查 - 发布 - 变更
为什么我们不会编写规范? 通过避免编写规范来节省时间,几乎肯定是一种虚假的节约行为。
20章:代码审查(挑毛病永远比提出正确的意见容易得多) 被审查人员的态度:愿意让别人帮助你在代码中寻找缺陷 审查人员的态度:对事不对人,要说『代码是。。。』而不是『你总是。。。』 审查的目的是通过集体协作来改善代码,而不是指责某人或者证明实现决策的正确性。
完美的代码: 没有bug;正确;完整(测试套件);结构良好;可预知;健壮;数据检查;可维护;
21章:项目估算 取决于你对任务理解有多好。 为什么预估这么难? - 大量变数需要考虑 - 需求变化 - 不知道所有相关工作 - 很少有项目从白纸开始,需要认真了解现有系统。 - 没有经验可以借鉴 - 依赖不可控的第三方
压力之下: 不要承诺一个智能拼凑代码的时间范围
实用的估计方法: - 将任务尽可能分解成最小的单位,高效地通过首次系统设计。 - 为每一个小任务提供一个时间范围估计,以『人时』或者『人天』为单位。不能大于『人天』单位 - 当你完成了所有时间范围估计后,将所有时间汇总。 时间包括:充分的设计;实验性工作和原型设计;代码实现;调试;单元测试;集成测试;编写文档;研究调研;
考虑问题: - 一个项目越具体,范围越明确,越容易估计。 - 功能越多,估计时间难度越大。 - 没有了解。不要猜测估值。 - 不同人做事速度不一致。 - 不要过于乐观,不要承诺不合实际的时间范围。 - 不要事先就计划加班加点。
计划游戏: - 缩短关键路径 - 没有大量并行任务 - 分解;(假期,工作量,偶发事件,集成,老项目支持,扫尾)
坚持: - 启动新项目时候检查分配的时间是否实际。 - 参考时间表;不断根据计划监视进展 - 必要时间内做尽可能多的工作,除此之外不要多做。 - 探求模块化的细致设计可以减少组件之间的相互依赖性。 - 编写优秀代码,就必须有一套全面的单元测试,合理减少排错和维护时间。留出时间写文档和全面测试。 - 留心变更的需求和设计规范,跟踪变更对时间计划的影响并且立即报告。 - 严格限制分散精力的事项。 - 保持积极乐观的方法。
22章:程序秘方:代码开发的方法和过程 构建工作方法和实践
编程风格:软件问题是如何安排解决的,以及解决方案是如何分解并使用目标语言构建模型的 两大阵营: - 命令式:一系列步骤的顺序 - 声明式:推理规则或函数
结构化编程:控制为中心,自顶向下 面向对象的程序设计:抽象(选择性概括);封装(只能通过良好定义的api访问);继承;多态
函数式编程: 逻辑编程
开发过程: 厚/薄;先后顺序;设计方向
瀑布模型:需求分析;设计和规范;实现;集成和测试;维护; ssadm和prince V模型: 原型设计 迭代和增量开发 螺旋模型 敏捷开发
理解方法以及正确使用他们,优秀的过程不会挡你的路
23章:编程领域大观:不同的编程分枝 应用程序编程:塑装软件和定制应用程序 游戏编程: 系统编程:操作系统;驱动程序; 嵌入式编程: 分布式编程: 网络应用程序: 企业编程 数字编程:科研领域
24章:结果好一切都好 杰出的程序员是拥有正确态度的人:一个在任何情况下都始终寻求编写最佳代码,能够与他人良好合作,并且能够在软件工厂的压力下做出正确决策的人。编码高手知道如何控制技术问题,并想办法在其成为软件陷阱之前尽早解决他们。
- 实践。
- 寻求高手的指导,学习高手的品质。
- 坚持编程,拓宽眼界。编写更多代码,尝试新技术,学会应对新语言、不同的语言和不熟悉的技术。
- 不怕犯错。以一种建设性的态度面对代码审查。
- 多关心外部世界,融入现实环境。
- 找到你的领域的经典书籍。
- 教育:向初学者传授你的智慧
- 拓宽你的技术视野,参加专业性组织。
- 享受编程乐趣,编写出卓越代码解决棘手的问题。