Part1 软件构建
软件开发的主要流程:问题定义-〉需求分析-〉规划构建-〉架构(概要设计)-〉详细设计-〉编码与调试-〉单元测试-〉集成测试-〉系统测试-〉交付或发布-〉软件维护
各个过程与软件构建的关系如下图:
为什么构建活动如此重要:
①构建活动是软件开发的主要组成部分。
②构建活动是软件开发中的核心活动。
③把主要精力集中于构建活动,可以大大提高程序员的生产率。
④构建活动的产物----源代码----往往是对软件的唯一精确描述。
⑤构建活动是唯一一项确保会完成的工作。
Part2 用隐喻来更充分地理解软件开发
隐喻是启示而不是算法,隐喻把软件开发过程与其他你熟悉的活动联系在一起,帮助你更好地理解。
常见的软件隐喻:
①写作代码:就像写信,不需要正规的做计划,想到什么写出来就是了。不行就丢掉。
②配置系统:逐步的设计完善系统,将麻烦减到最小,缺点是暗示了无法对开发软件的过程和方式进行任何直接的控制。
③系统生长:增量式开发,先做出软件系统的骨架。一个简单、但是可以运行的版本,然后逐步完成一个完全可以工作的系统。
④建造软件:软件开发就像建筑一样,需要计划,前期准备和执行。
⑤智慧工具箱:人们在多年的开发过程中积累了大量的技术、技巧和诀窍。技术并不是规矩,他只是分析工具。
⑥组合各种隐喻。
Part3 三思而后行:准备工作
主要准备工作倾向于集中改进需求分析和项目规划。
番外语录:(虽然经济景气程度时高时低,但是优秀的程序员永远是紧缺的。人生苦短,当有大量更好的选择摆在你面前的时候,在一个荒蛮的软件企业中工作是不明智的)
程序员是软件食物链的最后一环。架构师吃掉需求,设计师吃掉架构,而程序员则消化设计
修复缺陷的成本:
三种常见的软件项目种类,及其典型的良好实践
序列式开发法与迭代式开发法选择
①序列式开发法适用下列环境:
需求相当稳定。
设计直接了当,而且理解透彻。
开发团队对于这一应用领域非常熟悉。
项目风险很小。
“长期可预测性”很重要。
后期改变需求、设计和编码的的代价很可能较昂贵。
②迭代式开发法适用下列环境:
需求并没有被理解透彻,或者出于其他理由你认为它是不稳定的。
设计很复杂,或者有挑战性,或者两者兼具。
开发团队对于这一应用领域不熟悉。
项目包含许多风险。
“长期可预测性”不重要。
后期改变需求、设计和编码的代价很可能较低。
为什么要有正确的需求
明确的需求有助于确保是用户(而不是程序员)驾驭系统的功能
明确的需求有助于避免争论。
有助于减少开始编程开发之后的系统变更情况。
稳定的需求是软件开发的圣杯。
在构建期间处理需求变更
(对比需求核对表)评估需求的质量。
确保每一个人都知道需求变更的代价。
建立一套变更控制程序。
使用能适应变更的开发方法。
放弃这个项目。(需求糟糕不稳定,以上都不奏效)
注意项目的商业案例。
需求核对表
针对功能需求:
是否详细定义了系统的全部输入,包括其来源、精度、取值范围、出现频率等?
是否详细定义了系统的全部输出,包括目的地、精度、取值范围、出现频率、格式等?
是否详细定义了所有输出格式(Web页面、报表,等等)?
是否详细定义了所有硬件及软件的外部接口?
是否详细定义了全部外部通信接口,包括握手协议、纠错协议、通信协议等?
是否列出了用户想要做的全部事情?
是否详细定义了每个任务所用的数据,以及每个任务得到的数据?
针对非功能需求(质量需求):
是否为全部必要的操作,从用户的视角,详细描述了期望响应时间?
是否详细描述了其他与计时有关的考虑,例如处理时间、数据传输率、系统吞吐量?
是否详细定义了安全级别?
是否详细定义了可靠性,包括软件失灵的后果、发生故障时需要保护的至关重要的信息、错误检测与恢复的策略等?
是否详细定义了机器内存和剩余磁盘空间的最小值?
是否详细定义了系统的可维护性,包括适应特定功能的变更、操作环境的变更、与其他软件的接口的变更能力?
是否包含对“成功”的定义?“失败”的定义呢?
需求的质量:
需求使用用户的语言书写的吗?用户也这么认为吗?
每条需求都不与其他需求冲突吗?
是否详细定义了相互竞争的特性之间的权衡——例如,健壮性与正确性之间的权衡?
是否避免在需求中规定设计(方案)?
需求是否在详细程度上保持相当一致的水平?有些需求应该更详细地描述吗?有些需求应该更粗略地描述吗?
需求是否足够清晰,即使转交给一个独立的小组去构建,他们也能理解吗?开发者也这么想吗?
每个条款都与待解决的问题及其解决方案相关吗?能从每个条款上溯到他在问题与中对应的根源吗?
是否每条需求都是可测试的?是否可能进行独立的测试,以检验满不满足各项需求?
是否详细描述了所有可能的对需求的改动,包括各项改动的可能性?
需求的完备性:
对于在开始开发之前无法获得的信息,是否详细描述了信息不完全的区域?
需求的完备度是否能达到这种程度:如果产品满足所有需求,那么它就是可接受的?
你对全部需求都感到很舒服吗?你是否已经去掉了那些不可能实现的需求——那些只是为了安抚客户和老板的东西?
架构的典型组成部分
程序组织
主要的类
数据设计
业务规则
用户界面设计
资源管理
安全性
性能
可伸缩性
互用性
国际化\本地化
输入输出
错误处理
容错性
架构的可行性
过度工程
关于买还是造的决策
关于复用性的决策
变更策略
架构的总体质量
架构质量的核对表
针对个架构主题
程序的整体组织结构是否清晰?是否包含一个良好的架构全局观(及其理由)?
是否明确定义了主要的构造块(包括每个构造块的职责范围及其他构造块的接口)?
是否明显涵盖了“需求”中列出的所有功能(每个功能对应的构造块不太多也不太少)?
是否描述并论证了那些最关键的类?
是否描述并论证了数据设计?
是否详细定义了数据库的组织结构和内容?
是否指出了所用的关键的业务规则,并描述其对系统的影响?
是否描述了用户界面设计的策略?
是否将用户界面模块化,使界面的变更不会影响程序其余部分?
是否描述并论证了处理I/O的策略?
是否估算了稀缺资源(如现程、数据库连接、句柄、网络带宽等)的使用量,是否描
述并论证了资源管理的策略?
是否描述了架构的安全需求?
架构是否为每个类、每个子系统、或每个功能域(functionality area)提出空间与时间预算?
架构是否描述了如何达到可伸缩性?
架构是否关注互操作性?
是否描述了国际化/本地化的策略?
是否提供了一套内聚的错误处理策略?
是否规定了容错的办法(如果需要)?
是否证实了系统各个部分的技术可行性?
是否详细描述了过度工程的方法?
是否包含了必要的“买 vs. 造”的决策?
架构是否描述了如何加工被复用的代码,使之符合其他架构目标?
是否将架构设计得能够适应很可能出现的变更?
架构的总体质量
架构是否解决了全部需求?
有没有哪个部分是“过度设计”或“欠设计”?
整个架构是否在概念上协调一致?
顶层设计是否独立于用作实现它的机器和语言?
是否说明了所有主要的决策的动机?
第二部分 创建高质量的代码
Part4 关键的构建决策
选择编程语言
Part5 软件构建中的设计
理想的设计特征
最小的复杂度(Minimal complexity):设计的首要目标是让复杂度最小。要避免“聪明的”设计,因为“聪明的”设计常常都是难于理解的。应该做出简单且易于理解的设计。如果你的设计方案不能让你在专注于程序的一部分时安心地忽视其他部分的话,这一设计就没有什么作用了。
易于维护(Ease of maintenance):意味着在设计时为做维护工作的程序员着想。请时刻想着维护程序员可能会就你写的代码而提出的问题。把这些程序员当成你的听众,进而设计出能自解释的系统来。
松散耦合(loose coupling):意味着在设计时让程序的各个组成部分之间关联最小。通过应用类接口中的合理抽象、封装性及信息隐藏等原则,设计出相互关联尽可能最少的类。减少关联也就减少了集成、测试与维护工作量。
可扩展性(extensibility):是说你能增强系统的功能而无须破坏其底层结构。你可以改动系统的某一部分而不会影响到其他部分。越是可能发生的改动,越不会给系统造成什么破坏。
可重用性(reusability):意味着所设计的系统的组成部分能在其他系统中重复使用。
高扇入(high fan-in):是说让大量的类使用某个给定的类。这意味着设计出的系统很好地利用了在较低层次上的工具类(utility classes)。
低扇出(low fan-out):是说让一个类里少量或适中地使用其他的类。高扇出(超过约7个)说明一个类使用大量其他的类,因此可能变得过于复杂。
可移植性(portability):设计出的系统应该很方便地移植到其他环境中。
精简性(cleanness):设计出的系统没有多余的部分。任何多余的代码也需要开发、Review和测试,并且修改了其他代码后还要重新考虑这部分。
层次性(stratification):意味着尽量保持系统各个分解层的层次性,是你能在任意的层面上观察系统,并得到某种具有一致性的看法。
标准技术(Standard techniques):要尽量用标准化的、常用的方法,让整个系统给人一种熟悉的感觉。
设计的层次
常用的子系统:业务规则、用户界面、数据库访问、对系统的依赖性
设计构造块:启发式方法
寻找现实世界的对象
形成一致的抽象
封装实现细节
在可能的情况下继承
信息隐藏
找出容易改变的区域
保存松散耦合
探寻通用的设计模式