本章是将本书前面提到的一些抽象话题明确化—复杂性、抽象、过程、可读性、迭代等等。
征服复杂性
致力于降低复杂性是软件开发的核心。没有人的大脑真正有能力跨越9个数量级的细节。本书之前谈到的话题里也涉及了降低复杂性的方法。
- 在架构层将系统划分为多个子系统,以便思绪在某段时间内能专注于系统的一小部分(一个函数只实现一个功能)
- 保持类接口的抽象性,从而不必记住不必要的细节(变量命名同理)
- 避免全局变量,因为它会增加需要兼顾代码的比例(变量靠近使用的地方)
- 避免深度嵌套循环或者条件判断,用简单的控制结构取代可以占用较少的大脑资源
- 子程序应该保持短小
- 使用清楚、不言自明的变量名,从而大脑不必费力记住诸如i、j之类没有语义的参数
- 传递给子程序的参数数量应尽量少,只传递子程序接口抽象所必需的参数
- 用规范和约定使大脑从记忆不同代码片段的随意性、偶然性差异中解脱出来
-
精选开发过程
软件开发与其过程有着很大的关系。对于小的项目,程序员的个人才能对软件质量影响最大,对于多个程序员参与的项目,组织性的重要程度超过了个人技能。
人们一起工作时的方式将决定其能力是珠联璧合还是相互抵消,某个成员对其他人的工作时支持还是拖后腿,取决于团队采取的开发过程。
开发过程之所以重要,主要原因是软件开发中,质量一开始就要逐步建立起来。这与那种先拼凑代码,再通过测试剔除缺陷的思路相悖。后者完全是错误的,测试仅仅说明软件所用的特定方法有缺陷,并不能让软件更有用、更快、更可读或者更有拓展性。
遵照写伪代码再对其填充的代码的步骤,就能享受自上而下的好处。
计算机图灵奖获得者之一罗伯特·弗洛伊德说,我想对严肃的程序员说的话就是,要花工作时间的一部分来检讨和提炼自己的方法。即使程序员总是奋力赶进度,或者满足最后期限的要求,对方法的抽象是更明智的长远投资。
对于日常编程工作就可以体现在,构建新项目时,目录结构、功能拆分、数据管理、权限、登录等主要的全局功能、项目代码规范等等这些都需要在写业务代码之前,就构建好大体的轮廓,当然这些很多在公司自有的模板库中已经提供了。而在写一个影响范围比较大的功能需求时,不同方案的选取同样值得花时间去思考,这样提高质量的同时避免后期迭代如果打补丁。首先为人写程序,其次才是机器
计算机不关心你的代码是否好读,它更善于读二进制,而非高级语言的代码。编写可读性好的代码,是为了便于别人看懂。
可读性的正面影响: 可理解性
- 容易复查
- 错误率
- 调试
- 可修改性
- 开发时间
- 外在质量
可读的代码写起来并不比含糊的代码多花时间,运行时至少不比后者慢。
为了节省编写代码而不顾阅读它的时间是不经济的。专业的程序员要有总是写可读性好的代码的习惯。
一项研究发现,程序在重写前平均要经过10代程序员的修改。维护的程序员要花其50%到60%的时间,去搞懂他们要维护的代码。可见可读性的重要性。
本书前面也提到了一些技术帮助提高可读性:好的类名(及子程序名、变量名)、精心的布局、短小的子程序;将复杂的逻辑测试隐藏挂载布尔函数中;将中间结果赋值给变量使复杂运算清晰化等等。任何单一的技术对程序可读性的改善都是些许的,但许多微小的改进迭加起来,就能使程序面貌一新。
深入一门语言去编程,不浮于表面
不要将编程思路局限到所用语言能自动支持得范围。杰出的程序员会考虑他们要干什么,然后才是怎样用手头的工具去实现他们的目标。
借助规范集中注意力
日常编程中会遇到的问题,例如循环体缩进几个空格?怎样格式化注释?怎样排列标签的属性?这些问题都有若干中正确的答案。使用规范能够避免各程序员随意决定导致的理解困难,统一整个团队的认知。
规范是一套用于管理复杂度的智力工具。
规范的好处:
- 能够精确地传达重要信息。采用命名对方后,一个字符就能区分局部变量、类变量和全局变量;大小写字母能区分类型、具名常量和变量。
- 免除各种风险。例如,通过禁止全局变量或者一行里写多条语句,避免这些危险的用法。
- 增加了对低层工作的可预见性。例如消除了全局变量,使得各类和子系统之间潜在的交互消除,看程序的人不会因为全局变量变得没有把握,减少了必须消化的细节数量。
- 弥补语言的不足之处。对于不支持具名常量的语言(如Python等),规范可以区分用来读写的变量和用于只读的常量。
基于问题域编程
另一个处理复杂性的特殊方式就是尽可能工作于最高的抽象层次。其中一种方法就是针对编程所要解决的问题来工作,而非针对计算机科学的解决方案。
顶层的代码要说明想要解决的问题,应当包括描述性的类名和确切说明干什么的子程序调用,不应该有注释、数据如何保存等细节层的信息。
将程序划分为不同层次的抽象
你必须在某些层次上按照实现层的概念去工作,但是你可以隔离开工作在实现层次的程序部分和工作所在的问题域的部分。
问题域的底层技术
即使没有完整的、架构性的方法用于问题域,你仍可以使用本书中提出的技术来解决实际问题(非计算机科学的问题)。
- 在问题域使用类,来实现有实际意义的结构
- 隐藏低层数据类型及其实现细节的信息
- 使用具名常量来说明字符串和文字量的意义
- 对中间计算结果使用中间变量
-
当心落石
程序编制时,要有好的判断力,需要对程序细微问题的警告信息做出反应。
软件开发中,注意警告信息很重要?因为考虑程序的缜密程度会对程序质量有决定性作用,所以关注警告信息与否也直接影响到最终产品的质量。 当有人说这段代码暗藏玄机时,这是个危险的征兆,应考虑重写
- 类中含有比平均数目更多的错误也是个危险信号,同该考虑重写
- 设计度量也可作为警告,比如子程序中有多于十个以上的判断点、三层以上的逻辑嵌套、过多的变量、或者与其他类联系过于紧密、或代码内聚性不强,这些都应该引起怀疑
- 代码有重复,或者在若干做的修改很相似
- 不能很方便地单独使用某个类
- 不容易写注释、不容易命名变量、不容易将问题分解成接口清晰的几个类
迭代,反反复复,一次又一次
软件设计师一个逐步精化的过程,需要经过反复修正和改进。
高层和底层设计都需要反复,首次尝试的方案可能行得通,但往往不会一下子达到完美,通过几次重复和不同的方法,能得到有关问题的由单一方法无法得到的认识。
迭代法对代码调整同样有益。优化的过程不是凭直觉就能把我,需要反复调整、测量、再调整的反复过程。
汝当分离软件与信仰
非要坚持某种设计方法,笃信特定的布局或注释风格,极力避免全局数据。不管哪种情况,都是不合适的。
软件先知
革新的方法需要公开,才能让别人尝试,尝试之后才能充分证实或反驳。
不要盲目跟风,而应使用一种混合的方法。可用激动人心的最新方法做实验,但仍扎根与传统的可靠方法。
折中主义
要对编程问题找出最有效的解决方案时,应该有意识地尝试几种途径,只有实践才知道哪些好使。
一味地坚持某种方法,可能与最有效的方法失之交臂。应该将方法视为工具箱中的工具,工作时自己判断挑选最好的工具。应该不管提高自己挑选合适工具的能力。
试验
试验应该贯穿整个开发过程。
在软件开发方面,应该保持开放的心态,这样才能从开发和产品中有所收获。