2017 December 01 —— improvement; coding

Code Complete 精读

2 隐喻

比喻与类比是非常重要的表达方式,软件领域中各种形象的隐喻描述各种特定现象; 如: Bug ,不理解的东西结合类比可以产生更加深刻的理解,这种做法也称为建模;

随着科学的发展,一系列不太合适的隐喻逐渐被更好的隐喻替代;

隐喻的启示性非常重要,有方向指引作用,但不合适的隐喻会产生迷惑性;

算法的明确性与启示的模糊性,启示只有大方向,启发指引着如何去寻找有效信息;

善用隐喻对于编程的理解会更好,有助于问题抽象化,概念化;

软件开发的一些隐喻:

  • 写作
  • 培植系统: 耕作
  • 生长: 增量式,从骨架到血肉 => 从增量式开发到演进式交付到敏捷编程之类核心思想
  • 从生长到建造: 暗含了软件构建过程中的诸多阶段,建造计划,准备,设计,执行…根据要建造目标的规模其建造设计过程复杂度指数式增长;简单的建造试错成本低,复杂的建造几乎不允许出错;

技术不是限定构建实践的规矩与框架,而只是分析工具,编程的知识学习得越多,工具箱中(脑中)的工具越多,在具体实践时应该要知道何时使用何种工具,以及如何正确的使用这些工具,并不存在一个能适用于所有的工具,因地制宜的选择正确的工具并有效编程尤其重要

工具箱隐喻,各种语言各种技术都只是工具箱中的一个工具而已,具体问题具体分析,不要被自己熟悉的工具所限定 - 合适的时候拿来就用;

4 关键的构建决策

好的符号系统让大脑从非必要工作中解脱,聚焦于更核心的工作,编程语言的选择从多个方面影响生产率与代码质量;

你思考的能力取决于你是否知道表达该思想的词汇

编程约定: 复杂程序中的架构指导方针,各个具体部件能反映整体架构的内涵,整体与局部之间充斥着各类风格将导致系统混乱而又邋遢,不要让自己的系统成为一幅”拼接图”,到处充斥着补丁;

看清所处在技术浪潮中的位置;

编程工具不应该决定你的编程思路;”在一种语言上编程”与”深入一种语言编程”的思想对比:在一种语言上编程的程序员思想限定于语言支持的那些构建,而深入一种语言编程者首先想清楚要表达的思想是什么,然后决定如何使用特定语言提供的工具来表达这些思想- 先确定思想,再选择语言去表达思想,如果你所使用的语言缺乏相应的特性,那就应该自己定义约束,编码规定,标准去弥补他; => 深入一种语言编程的思想非常重要,时刻提醒自己所采用的编程实践是对所使用编程语言的正确响应,还是受编程语言的控制,切勿被语言支配,被工具支配

5 软件构建中的设计

设计与构建的界限模糊,设计可以详细到让编码实现工作成为近乎机械无脑的体力活;通常正规架构只解决系统级别的事项,而将大部分设计工作与构建实践同步进行;

设计是将需求分析和编码实践工作联系在一起的中间工作;

设计并不是一开始就是完美的,仅仅依靠需求分析就推倒出完美合理的设计师不现实的,应该始终记住 合理的设计也是经过不断的修订与迭代完善才产生的;(但需求分析得越透彻,设计过程中犯的错会越少)

设计是一种必须通过解决或是解决部分才能被明确的问题;(问题必须先被解决才能发现真正的问题是什么)

设计阶段的关键是敢于尝试敢于犯错,由于设计阶段的试错代价小,尽可能在设计阶段多踩坑;设计永无止境,很难判断设计是否真正充分,合理,细节考虑的足够多;(直到设计时间用完);

在软件开发过程中,经常遇到各种矛盾的问题,就像要时间还是要空间这类,我们所要做的就是合理的去取舍,分析什么事真正需要的;而设计就是确定取舍以及调整权重,顺序的过程

设计是在不断的设计评估,非正式讨论,写实验代码,以及修改实验代码中逐渐演化与完善形成

  • 管理复杂性以及如何应对复杂度;

软件开发本质性困难:

  • 现实问题解决方案中,软件中定义现实实体之间的交互行为的复杂性;

没有谁的大脑能容得下一个现代计算机程序,作为开发人员不应该试着把整个计算机程序都塞进自己的大脑,而应该试着以某种方式去组织程序,以便能在一个时刻专注一个部分;在架构层次上通过将系统分解为子系统来降低复杂度;子系统之间的相互依赖越少,你就越来越容易在同一时间里专注问题的一小部分,精心设计的对象关注点相互分离,从而使你能在每个时刻只专注于一件事;

复杂度管理是软件开发过程中最重要的;

  • 设计层次: 从系统到子系统模块到类到类结构,最后到子程序实现;

定义好松散合理的子系统模块后还需要给模块的通信定义规则,否则模块之间无限制的通信将导致整个系统的零散(将系统之间的连线类比为水管,好的设计是替换某个子系统时,重新连接水管少,二次构建快)

通过限制子系统之间的通信来让子系统的存在更有意义;不加限制的子系统通信将导致子系统的分解变得无实际意义;

设计构造块-启发式

本节干货非常之多,尤其值得反复阅读

  • 软件设计是非确定性工作,并不是存在如果做了什么就能得到结果这样的定式,软件设计充满了不确定性,使用一些启发式方法来进行试验,试错;

  • 构建系统与现实模型之间的映射,找到现实世界的对象(传统面向对象编程),需求分析越详细,现实模型构建越完善;

  • 形成一致抽象: 抽象赋予了聚焦核心的能力,使你可以忽略无关细节问题;(站在更高的层次看问题更加清晰)

  • 封装实现细节

  • 信息隐藏

再次回顾之前提到的管理复杂度,这些都是为之服务

  • 找出系统中容易改变的区域,隔离;

  • 保持松散耦合: 一个模块越容易被其他模块调用, 那么他们之间的耦合关系就越松散;反之如果对于一个模块的使用要求关注的点越多(内部细节?全局数据的修改?不确定的功能点?),这样系统整体的复杂度就越高,其模块内部管理能力也就被大削弱;

设计实践:

迭代: 迭代优化不要停止,只要开始迭代总会逐步优化设计

分而治之: 系统分割,分割到能用代码很明确的实现

抽象: 自上而下,自下而上

建立实验性原型验证设计: 快速构建最小可视化原型;

记录设计成果: 记录 wiki,写总结构建 …

6 类

以所用语言编程,但思路不受其限制

ADT:
先考虑 ADT 而后再考虑类是深入一种语言编程的优秀实践;

接口:
好的接口是高质量类构建最重要的核心,通过借口来展示合理的抽象,并将细节隐藏在抽象背后;抽象提供以简化形式看待复杂操作的能力,而接口则为隐藏在背后的具体实现提供了一种抽象;

接口与抽象类相比是一种自上而下的抽象,定义了子程序工作的目标,接口中每个子程序(抽象函数)都朝着这个目标工作;

类接口抽象层次的一致性; 在考虑类时始终将类看作是 ADT 的实现机制,每个类应该有且仅实现一个 ADT;继承在某种程度上是 is 关系的提现,如果类间关系没有 is, 则可能造成类间关系混乱;

真实理解类所实现的抽象,究竟是对谁进行的提炼与抽象;

慎重修改与扩展,谨防破坏接口的抽象;随着代码的腐朽一些接口可能逐渐变为零散功能的大杂烩;

关注接口的抽象层次有助于理解类的设计,时刻反省这个类是否表现为 一致的抽象,每当添加接口中的子程序时,就应该检查这个子程序是否与接口抽象一致性;

封装:
抽象通过构建让你忽略细节的模型管理系统复杂度,而封装则屏蔽细节;抽象与封装是双子星,要么同时出现,要么都不出现;封装性一旦破坏,抽象能力通常也会开始遭殃;

类的访问性控制;
尽可能少公开暴露成员属性;

保持可读性- 代码阅读的次数要比编写次数多得多,让代码保持可读性尤为重要;

控制子程序的公开与否仅仅由接口的抽象一致性决定;

类的使用者的假定条件不应过多;

语义上破坏封装性,体现为整体逻辑混乱,代码的调用不应该依赖于类的具体实现;

针对接口编程:一旦开始通过查看类的内部实现才能逐步了解类的使用方式,就开始不是针对接口编程,而是透过接口而直接通过的内部实现来编程

注意存在的类间过于紧密的耦合关系;

设计与实现:

  • 组合优于继承
  • 警惕超过7个数据成员的类=> 通常人类做其他事情时能记忆的离散项目数量是 7 +/- 2;过多的数据成员,需要分解;

继承要么被配合详细的说明使用,要么就不使用,不使用继承的类最好明确标注;(final)
只继承需要继承的;
不要覆盖不可覆盖的成员;(java private 覆盖) ; 利用空实现覆盖某些行为,是值得特殊考究的方案,是不是可以通过重新组合类间关系实现?

成员函数:
减少对其他类子程序的间接调用;如 A.B.C.d(); 通常应该尽量减少类间合作范围;

为什么要创建类:
现实世界对象建模
抽象对象建模
建立中心控制点 => 中心化与去中心化…
减低复杂度
隐藏实现细节
代码更加易于重用

避免万能类的创建;
消除无关紧要的类;
避免动词命名而构建的类;- 一个类只有行为而没有属性通常不是真正的类;

7 高质量的子程序

看看那些有问题的子程序,子程序的重要性;

为何创建子程序:

降低复杂度,一旦子程序构建完善,就可以直接调用子程序而忽略细节,子程序的这种抽象能力是复杂项目管理核心,当子程序内部过度复杂时,暗示需要提取新的子程序,子程序这一层中间抽象,对于凝练代码非常有效;

很多看起来似乎简单,而偷懒不构建子程序,在后面由于简单的操作组件业务复杂化,整个逻辑也就开始迅速腐烂了;

子程序的设计:

最优设计: 每个子程序只做一件事,把一件事做好就足够,这样可以使子程序内部高度内聚,同时也能获取更高的子程序可靠性;

内聚: 功能内聚(最佳实践)/ 顺序内聚(执行顺序) / 通信内聚(数据通信) / 临时内聚(业务聚合)

子程序名称:

避免无意义,模糊亦或是表述不清晰的动词组合: handle… 等, 子程序本来就是处理事件的.. handle 毫无意义

子程序名称:应该清晰易懂,长度9-15字符数;

名称应该对返回值有所描述;

功能内聚性的子程序通常是针对某对象执行某操作 => 名字可以是 强烈语气动词 + 宾语(对象)

使用对仗词语: old/new add/remove

子程序代码长度:

一般最优为50-150,屏幕一到两屏之间,超过200行的代码可读性方面容易出现问题;

子程序参数控制:

子程序之间参数通信问题是最常见的内部接口问题;

参数定义顺序: 按照输入/ 修改 / 输出参数有序定义;

类似函数使用了类似参数,应让类似参数排列顺序一致;

传入的参数要被使用,删除未被使用的参数;

传入的参数通常不应该被作为工作变量被修改,如不恰当的储存临时工作结果,临时结果引入临时变量,澄清入参在子程序中的角色成分;

依照之前说明过的类数据成员应该在 7+/-2,同样依照人类的零散记忆能力有限,参数也应该控制在7个以内;如果子程序之间互相调用时总是各种参数互相传递,很可能是是程序结构出现了问题,模块之间耦合过于紧密;

函数与过程之分:

函数代表有返回值的子程序,而过程则是没有返回值的子程序: 如果一个子程序的用途是返回其名称所指明的返回值,则应该使用函数,否则应该使用过程

这一部分每个章节后的核对表非常有效,应该经常用于自省自检

8 防御式编程

起源: 防御式驾驶,在驾驶时你永远不知道另一位司机要做什么,如何确保其他人做出危险动作时自己不会受伤;

在面对复杂的非法数据世界,如何保证自己所编写的子程序不因其他错误而遭到破坏;程序员应该在确认程序都会有问题,都需要被修改之后,进而根据这一点前提来进行防御性编程;

好的子程序应该中断垃圾产生流程,不让垃圾毁灭整个系统,让垃圾的生命在系统中总结,垃圾进垃圾出是缺乏安全性的低质量程序的标志;

  • 检查外部(服务器,文件,用户,网络…)来源数据值,对于非法外部来源数据进行处理
  • 检查入参
  • 处理错误参数

最佳实践: 最好一开始就不要在代码中引入错误,使用迭代式设计,伪码先行设计,TDD, 底层设计检查等方式进行错误有效预防;

断言: 开发与维护助手,帮助理清假定,但在发布版本的代码中可以不进行断言代码构建,以免影响系统;

通常断言用于处理永远不该发生的系统状况;

错误处理:

  • 返回一个代表错误的中立值,而不直接抛出异常;如 return -1等等常用措施
  • 返回上一次调用的老数据结果
  • 换用接近的合法值,如温度计测量值在0-100,检测到低于0则直接返回0;
  • 记录异常警告于日志文件
  • 返回一个错误码,错误由系统中的错误处理模块进行统一异常处理,如 java 中的异常抛出机制,层层抛出;
  • 采用合适的方式局部处理错误- 哪里产生错误就在哪里处理,灵活性高,但各个程序员处理异常方式差异性可能导致各类错误处理代码散布在整个系统之中;
  • 错误发生时显示错误信息
  • 关闭程序

应用程序正确性与健壮性的权衡处理机制;正确性代表永远不返回错误结果,即使关闭程序;而健壮性则意味着不断尝试某些措施,即使返回错误结果,也要让软件可以持续运转下去;

错误的处理机制是一种架构,一旦选定了一种错误处理机制,就应该在整个系统中一致处理,始终贯彻;

系统调用后的错误检查机制:一旦检测到错误就记录错误代号以及错误描述信息,切勿随意忽略错误

异常处理:
将代码中的异常事件传递给调用方的一种手段 – 语言层面的支持性;

正确的异常机制使用,有助于降低系统复杂度,滥用则可能让代码难以理解,甚至让系统到处充满危险

  • 异常是一种优秀的错误通知机制,用于通知其他部分发生了不可忽略的错误,而不像其他错误处理可能导致错误在不知不觉中蔓延; — 这是异常非常重要的一个特性(Kotlin暂时没有)
  • 不能用异常推卸责任,能在子模块局部处理的异常就应该在子模块中进行异常处理,而不应该抛出去;
  • 只在需要抛出异常的情况下才会抛出异常,调用程序需要了解被调用系统中的异常机制,异常本质上时弱化了程序的封装性;
  • 在恰当的抽象层次中抛出异常,抛出的异常也是程序接口的一部分,应该展现出一致的抽象;确保抛出的异常与子程序抽象层次的一致性;(合理的异常封装)
  • 抛出的异常应该包含异常产生的全部信息;
  • 尽量避免空的 catch,即使对于无法表现为调用方抽象的异常,至少使用日志记录;
  • 了解所用函数可能抛出的异常(函数如果抛出异常应该声明或者在 Doc 中说明)
  • 创建集中式异常报告机制(格式化异常信息,记录异常日志等)

隔离程序:

容损策略,保证其他部分的正常运行,防止错误蔓延整个系统导致系统性崩溃;(防火栏,隔离仓的现实抽象)

定义安全区域边界,定义好哪些地方是安全区域(数据都是合法安全的),哪些系统区域是非安全区,对于进入安全区的数据统一进行数据安全性校验,一旦数据异常非法则做出对应的反应;

通常为实现安全隔离,我们需要将程序实现分层,通过层次的思想去实现,非安全数据层,数据清理层(隔栏),数据安全层,进而可以让整个系统摆脱处处安全校验的问题,一旦数据通过了隔离层我们就可以认定数据已经是安全的;

隔离层的使用提现了架构层次上规定应该如何处理错误的思想;

辅助调试代码:

  • 不要把产品版本的限制强加到开发版本之上;开发过程中可以牺牲一些速度与资源使用已换取一些可以让开发更加顺畅的内置工具

开发过程中应该尽可能的让问题暴露出来,让问题越大越好,以便问题被修复;

  • 利用版本构建工具,区分构建,不将调试代码带入产品 Release 版本中;

取舍在版本中保留多少防御代码:

通常我们在开发阶段对于错误是希望尽可能暴露以便能尽快修复,而在产品发布后则希望错误尽可能的不引起用户的注意;

  • 区分哪些部分可能承受未检测出错误的后果,哪些部分不能? 保留哪些严重错误相关的检测代码
  • 去掉检测细微错误的调试代码
  • 去掉在开发阶段检测错误可能导致程序崩溃得代码
  • 保留一些可能让程序如你所期望方式崩溃得代码,以便进行整体系统检测
  • 以友好的格式,记录错误日志

考虑好系统中哪些地方需要防御?然后因地制宜的调整你的防御措施.防御式代码过多可能导致系统运行缓慢,系统复杂度变高,过少则系统可能健壮性不足;

9 伪码

以迭代形式构建类,其创建流程: 类总体设计,内部特定子程序设计,子程序构建,复查类的构建结果;

类的定义: 核心在于确定类的职责,定义类要完成什么职责,定义类要封装隐藏什么,更要精确的定义类的接口所表示的抽象;

子程序的创建: (迭代)设计,检查设计,编写子程序代码,检查代码(回顾设计-> 循环)

伪代码

描述工作逻辑,非正式的,类自然语言;通过书写伪代码进而达到更高效的创建程序代码的目的;

伪码原则:

  • 自然语言描述特定操作
  • 避免使用目标语言语法元素(伪码本质是一种抽象,是在具体实现层级之上,一旦使用目标语法元素,则又将其拉到编程实现同一层级,进而造成语言层面的语法约束,失去了高层级的精确描述特性)
  • 基于意图实现-> 用伪码描述问题解决的方法意图,而不是描述如何利用目标语言进行实现
  • 逐步降低伪码的的层次,通过层层迭代丰富伪码细节,最终得到具体的程序实现

核心优势: 伪码简化代码评审工作,无需检查源码就可以评审复杂的细节; 伪码实际上是一种迭代思想的具体体现,从一个高层逐步精炼化,具体化最后化为具体实现;这种小步前进的方式,便于程序员从高层抽象推进到具体时,反复检查当前已有的设计思路,避免高层次的错误影响具体实现,高层次的错误得以在高层迭代时解决;

伪码到具体实现:

如何利用伪码创建子程序:

  • 检查先决条件,子程序要做的工作是不是已定义好,改工作如果设计实现完成是否与整个系统相匹配,该程序的实现是否真的有用,是否真的有必要实现?
  • 定义子程序要解决的问题: 对于要解决的问题应该描述得尽可能的详细(明确需求),应该至少说明以下信息:

    • 命名? 功能的描述性?
    • 输入?
    • 输出?
    • 程序调用前的前置条件是否成立?
    • 子程序结束时后置处理是否完成?
    • 隐藏什么? 暴露什么? 封装什么?
    • 子程序的错误处理?
    • 子程序的执行效率?对系统的影响?
    • 子程序正确性的验证? 如何测试?

对于子程序的设计,应该可以用一句话来概括子程序的作用,用于澄清对于子程序的理解工作; 这句话的凝练非常重要!!通常位于子程序文档的第一句,一旦这句话无法顺利概括出来,子程序的系统中角色应该再次重新定义; 这句凝练的指导性方针语句应该立足于一个较高的系统层次进行描述;

考虑子程序的数据;

复查伪码:在写完伪码后复查你所构建的伪码,利用小黄鸭法则,想想你该如何对别人解释描述这段伪码;

迭代优化伪码,最初的伪码可能存在诸多不相干甚至有错误信息,在一次次复查之后应该只留下最精炼的正确伪码;

伪码转化为注释,进而根据注释指导,填充实际编码,在实际编码过程中检查代码是否需要进一步分解;

检查代码:

阅读自己所编写的代码,进一步进行复查,在脑海中检查程序错误(也称为桌面评审,我习惯在本地 commit 代码时,利用小黄鸭法则自行 Review 自己的代码,如果自己都不能说服就不要提交,对于提交的代码要有追求)

找其他人 Review 自己的代码(同行评审);

程序员应该理解自己所编写代码的每一行,知道为什么需要这行代码,没有什么会因为它看上去可行就是正确的; 只写出一个可以工作的子程序是不够的,如果不知道为什么可以工作,那就去研究它,讨论它,一直到弄明白为止;

在合适的时间去编译他,而不是一开始就编译,一旦进入挣扎着编译修改再编译的循环过程,最终的实现可能是拼凑着的奇特实现;

逐行调试,进一步了解自己所编写的每行代码,确保每行代码都如期执行;(其实是进一步确认每行代码自己都被正确理解)

测试代码的正确性;

消除错误: 一旦发现错误就应该将其消除掉,一旦发现所开发的程序漏洞百出,就应该及时终止东拼西凑的修改,重新从头设计重新构造子程序;

收尾工作:

进行最后的程序复查工作,接口如何? 设计质量如何? 变量? 循环以及逻辑? 逻辑的排列? 文档以及注释是否正确表达,有无歧义(错误的注释是一种误导,在修改程序是更新文档非常重要)?

高质量的编程是一个循环迭代的过程

系统考虑

27 程序规模对构建的影响

从小型项目到大型项目复杂度指数级的增长;

  • 交流与开发规模,随着项目成员数量的增加交流路径数量随之增加,大致与项目成员数的平方正比;(人员增多时,沟通爆发式增长)利用文档效率化交流;(统一式的文档交流,否则交流形式多样化容易导致混乱)

  • 项目规模增大,缺陷密度随之增加;
  • 项目规模与生产率: 小型项目中生产率更多与程序员个人能力直接相关,随着项目则庞大化项目的组织方式开始越来越重要(论架构的重要性)
  • 项目规模与开发活动: 随着项目规模增大,架构,继承,设计,调试,测试时间都会按照比例增长,而不再是小项目那样构建活动占据绝大部分时间;

其他有益实践: 有训练的编码实践, CodeReview, 好工具的支持,高级语言支持;

  • 方法论与规模: 方法论这类形式化的东西对于大型项目管理非常重要,但需要明确的是你应用这些方法的根本目的是为了进一步完善系统的管理;从轻量级方法论出发拓展以适用于你的大规模项目通常会比从重量级完善的方法论出发缩小到试用于实际项目;(适量级方法论)

28 管理构建

  • 鼓励良好的编程实践

定义好的标准

哪些好的管理实践?

  • 配置管理

  • 需求变更与设计变更

  • 代码变更

  • 评估 与度量

评估: 确定评估目标 预留评估时间 评估师应该对于需求有清晰的认知 对于项目活动的细节考察越多评估越详细 多项目评估取综合 定期重新评估(类似清理 todo)

度量: 量化是关键,科学的编程中量化是非常重要的特征;在度量一些环节时,会更加理解这个环节的特征,而不是模棱两可的可能似乎看起来这些形容词;

规模的度量 整体质量的度量 缺陷的度量 可维护性的度量 生产率的度量
  • 程序员与管理人员都是人,管理程序员,管理你的管理者(上下沟通)

程序员之间差异明显:个体天分,努力程度导致后续即使是同样工作年限的程序员差异非常之大;

团队差异: 不同团队在软件质量以及生产率上差异明显,好的程序员倾向与好的程序员在一起,差的程序员同理;(理念很重要,环境很重要)

程序员的信仰:…. 编程风格,IDE, 等等

软件工艺

31 布局与风格

  • 代码格式化基本原理: 应该展示代码的逻辑结构
  • 注意代码缩进所代表的语句从属含义,不使用错误的缩进增加逻辑结构的复杂度(嵌套层级增加)

正确的控制结构块:

// 纯块结构 
if(expression){
    one-statement;
}

// 括号指定块边界
if(expression)
    {
    one-statement;    
    }
  • 复杂条件表达式的排列构造,恰当的换行分割条件;

  • 类的布局
    • 除非你有充足的理由,否则一个文件只放一个类;
    • 通常按照: 类头部注释 构造函数 类数据 类 public 程序 protect 程序 private 程序顺序存放

32 自说明代码

  • 编程风格做文档: 自说明代码,编程风格承担了文档的大部分任务;

对于精心编写的代码而言,注释不过是美丽衣裳上的小饰物而已

自说明:

  • 子程序
  • 数据名
  • 数据组织
  • 控制代码块
  • 程序布局与程序逻辑结构表达
  • 程序设计

注释: 不好的注释对于程序的理解有反作用

注释应该在更高的层次解释代码究竟想干什么? 应该像书的标题那样提纲挈领,帮助程序员快速定位到他所需要的;

  • 写注释对代码审查很有效,省去一遍遍的解释自己的代码究竟是干什么这一繁琐的事;
  • 写注释能让你更好的思考,你究竟要做什么,如果注释编写困难,通常是你没有正确的理解代码,需要再思考的标志

  • 利用伪码编程法可以减少注释时间,无需专门花时间去编写注释
  • 在程序编写时同步构建注释,而不是在编写结束后专门抽时间去注释(注释是编程思维的指导)
  • 注释通常应该聚焦为什么要这么做,而不是怎么做(How),如果代码写的足够好,说明怎么做的注释通常是冗余的
  • 一些非常规做法应该使用注释说明
  • 注释不要使用意义不明的缩略语
  • 对于控制代码块的注释说明,以及嵌套代码块的 end 注释,标明嵌套结构;(代码块目的以及代码块结尾)

  • 子程序注释不应该有庞大的注释头
  • 记录算法来源

程序注释以书本为范例

  • 序 = 文件头 : 介绍程序作用
  • 目录 = 顶层文件 类文件 子程序 : 章节信息
  • 节 : 子程序内各单位
  • 交叉引用 = 参阅: 代码参阅 JavaDoc

33 个人性格

软件工程师,在设计构造软件时的基本建造材料就是其聪明才智,可利用工具就是自身 - 属于非常纯粹的脑力劳动之一;

编程工作本质是无法监督的一项工作,能否成为好的程序员 全凭自己;

利用恰当的编程方法来减轻脑力负担:

  • 分解系统,让系统易于理解
  • 代码审查,减少错误(与他人沟通,提升自己的软件质量)
  • 程序 less is more ,精简的程序减轻大佬负担
  • 基于问题细节来实现编程
  • 应用规范,让大脑从编程事务中解脱

求知欲:

  • 培养自身求知欲与把学习当做第一要务
  • 对不了解的过程做试验来观察特性: 在调试中加深自己的理解
  • 查找阅读解决问题的有关方法: 随意造轮子可能造出方轮子
  • 实践行动之前作出分析与计划: 想清楚再动手
  • 研究,学习成功的项目开发经验: 阅读高手代码
  • 阅读好书
  • 与专业人士沟通交往
  • 知道什么样的人是专业的,订立自己的专业目标

诚实:

  • 承认错误
  • 不是高手不装高手
  • 透彻理解所编写的程序
  • 提供真实的进度状况报告
  • 正确评估项目需求与进度,更重要的是学习如何坚持自己的立场

创造力与纪律:

  • 要有自我约束意识,大型项目应有其标准与规范

懒惰:

  • 拖延不喜欢的任务
  • 迅速完成不喜欢的任务,进而摆脱
  • 编写某个工具完成不喜欢的任务,以便再也不用做这些事情

以上是懒惰的三层境界,推崇第三种聪明的偷懒

经验:

软件行业知识推陈出新尤其快,如果不能放弃之前的一些思维定势,以及旧机器上的编程模式,在新时代来临知识,这样的经验还不如没有的好,不要成为语言的奴隶;

如果程序员过了两年没有将一门语言学好,再加三年也没有意义;

自省: 问问自己如果工作十年,你是一年经验10次重复还是真正的10年经验?

习惯:

好的编程习惯非常重要: 时时刻刻在影响你的编程行为以及思考模式

任何日后出色的程序员在前几年就做的很好,从那以后程序员的好坏就定型了 - 对于习惯要善于自省,善于完善自己的习惯;

在最初做某事时就应该端正自己的学习态度,因为在最初的时候,你会积极思考,轻松决定做的是否好, 一段时间后,习惯就开始发挥其作用了;

用更好的习惯代替旧的坏习惯,而不是用没有习惯代替坏习惯,不必为坏习惯多虑,有了新的习惯,坏习惯自然会被代替;

上一篇
下一篇
Loading Disqus comments...
Table of Contents