当我们处于蒙昧之中时,往往并不知道当为不当为之准绳,其结局必然是在沉重中惶恐,在惶恐中希冀,在希冀中重新陷⼊迷惘。

任何软件开发,其输⼊是需求,⼯具(编译⼯具)和⼈,这三者在特定的时空背景下受主观的影响⽽改变的可能性很⼩,接下来,基于上述三者,通过选定的管理⽅法流程、开发模型、需求开发⽅法估算⽅法、设计编码⽅法等等,对软件进⾏构建,期望最终达成多重质量目最终输出的是软件产品。对软件产品的度量⾄少有两个维度:⼀是⽤户层⾯如功能正确,性能优异;二是结构层⾯的,如容易维护等。

完美软件开发追求这样一种状态:在只考虑技术因素的时候,在限定的要求、⼯具、指定的⼈员状况下(输⼊),在既定的质量水平(输出),⽣产效能最⾼。在这种状态下,任何对管理、流程、估算⽅法等在尺度上的修正,如果不以质量降低为代价,都将导致生产效能的降低。

我们假设⼀个⼈的⼯程素养为E,⼯作意愿为W,组织所能提供的⼒量为O,内耗系数为M,那么对于⼀个拥有n个⼈的团队,其在单位时间内最终可能贡献值可以表⽰为:
[(E1*W1 +O)+(E2*W2+O)+......+(EnWn+O)]*M
其中M的取值可以为0到1。0表⽰完全内耗掉,整个团队完全没有贡献

软件与代码相等价,是同⼀事物的⼀体两面,也即说代码可以表征软件的⼀切价值。
如果项目管理确实有意义,那么项目管理必须直接或间接的对代码施加影响,而显然的,项目管理很难直接对代码产⽣直接影响,这也就意味着项目管理⾃⾝并不直接创生价值,必须以他⼈为媒介才可能最终产⽣⾃⼰的价值。

项目管理者对他⼈所可能产⽣的影响可以分为两类,一是对个⼈的影响;一是对组织的影响。

  • 对个⼈的影响可以体现在对消极、排斥异⼰、懒惰、好⾼骛远等情绪的遏制上,也可以体现在肯定成绩、表达信任这类激励⽅法上 !
  • 对组织的影响则可以体现在对谋⽽⽆断,职责不清、流程不清这类负⾯组织⾏为的遏制上,也可以体现在创建共识、对组织意识形态进⾏这类避免纷争的⼿段上。 上述两点正是项目管理的价值根源。

如果我们认可外部环境始终在发⽣着变化,那么不断的改变⾃通常就是必要的。与这种必要性相对的是----很多时候⼈们讨厌变化
社会变⾰中这种反对⼒量其根源也许更加复杂,团队中这种反对⼒量⼤多于⼀种安全的假象以及对思考和⻛险的厌恶。

潜意识中很多⼈认为对于这种⾏之已久的⽅既然他没导致任何问题,那对项目⽽⾔这种⽅法毕竟它不会导致较⼤的失败,与此同时,任何改变在可能获得收益之前,⾸先要⾯对的则是⻛险。这可能是很多⼈所不愿意承担的

暧昧与直来直去上的共识

  • 权利⽆论⼤小,往往令⼈迷恋。直接与上级论及其⾃⾝的是非,往往会引致上司的不快。⽽上司往往拥有对工资,奖⾦等的影响⼒,如果上司不能公⼼公论,那么⽆疑的后果是严重的。 上⾯讲的是最简单的⼈都会想到的事情,于是很多事情趋于暧昧,琢磨上司的心理成为一种必须功课这种氛围旦形成组织就会逐渐往“堂”的⽅向靠拢 所以我们有理由呼唤一种直来直去的⻛格,软件团队是做开发的,是⾮⾯前不需要太多的含蓄。 这对管理者的要求是,一定要确保没有⼈会因为直接发表观点受到任何形式的批评。对于管理者⽽⾔破坏一种直来直去的风格,实在是再简单不过的事情。

具体似是而非的共识
当我们设⽴立⼀一个目标或分解⼀一个任务,目标或任务的接受者对这个目标的理解越没偏差,团队的行进⽅向越不会出现偏差。这个时候最可怕的事情是容忍【差不多就是这个样子】这类的想法,这样会导致最终每个⼈人不是在 同⼀个⽅向上出力。

具体来讲,在物质层⾯“势”⾄少要具体体现为两类东⻄:一是职位以的提升/下降;一是技能的提升,在精神层⾯“势”,则可以具体体现为成就感,荣誉等。

构建软件的直接知识可以被分为三⼤大类:

  1. 用于打造概念边界并且优化概念间逻辑关系的知识。⽐比如,⾯面向对象分析和设计。
  2. 用于实现概念和逻辑的通⽤用领域知识。⽐比如编程语⾔言。
  3. 用于解决指定领域的专业的领域知识。⽐比如图形算法。 在很多场合3并不是必须的,但1和2则是不可分割的,并不存在孰轻孰重的问题。没有概念⽆无法形成逻辑,没有逻辑,概念的彻底定义更是⽆无从谈起。⽽而没有载体(⽐比如编程语⾔言或UML)概念和逻辑根本就⽆无法表达。事实上编程语言的演化很⼤大程度上是想把这种表达变的更容易。

⽽而与软件有关联的间接知识则有4⼤大类:

  1. 需求开发和描述。⽐比如规格说明的编写。
  2. 估算。比如功能点方法等。
  3. 测试。比如验收测试等。
  4. 软件工程和⽅方法论。比如 CMMI 和敏捷。

以职场⽽而论,公司的诉求往往单一⽽清晰,大多时候是以平衡短期及⻓期利益为根本诉求,但个⼈的诉求却往往复杂⽽含糊,每个⼈的诉求即有共通之处,也有差异。如果在项目管理这层次上漠视这种差异性,那个⼈诉求很可能就永远停留在不可⻅的区域⾥,这还谈什么双赢。

欲望的⽆无边界特质
欲望的起源有很多,但最终⼤大多以思维为媒介表达出来。在这⼀一过程中,思维的无边界特质直接导致了欲望的⽆无边界化。比如说:⼀个⼈人可能期望拥有整个宇宙。这最终导致到达一定程度之后,物质⼒力量将不⾜足以成势,精神的事情还是要回到精神层⾯面来解决。这个时候⼈人们很可能更需要成就感,需要⾃自我实现的空间。 但对刚毕业的⼈人⽽而⾔言,这些则相对遥远。

这时候协调各种意⻅成为项挑战性⽐较强的工作如果不能迅快的协调各⽅意见,那么组织很容易就变成袁绍型组织:会议很多,⼯作没进展进⽽产⽣巨大内耗。

一个团队中存在着两个经理,一个偏于技术,一个偏向于管理,两⼈层级相同,互不统属这种情况下,坏处有两个,个是项目信息将被割裂成两个部分。两个经理必然要做责任划分,旦划分之后就不再有个⼈对项目整完整的了解,但决策实际上同时需要两个维度上的信息,这必然会增加判断出错的⼏率,另一个坏处是两个经理对某些事情看法上可能会有分歧,这种分歧可能并不关键,从项目⾃⾝来看有可能都是可以但在不好的氛围下,让任何⽅放弃⾃⼰的想法,都可能需要相对⽐较高的成本。

当组织中的人员达到自为(共识较多)的状态时,管理的⼤大部分意义和功⽤用将会消亡,因此:

  • 从⽅向性上来看,管理的完美状态定要体现为管理自身的⼯作持续降低。从反⾯来讲就是,如果组织中需要管理的事情越来越多,并非证明管理越来越有效,⽽是佐证了管理正逐步⾛向失败。管理越少等价于说⾏⾛中的那辆既不依赖于推⼒也不依赖于拉力(激励、批评等),⽽是靠着⾃⾝的动⼒
  • 从个⼈的⾓度看,组织中的个⼈要越来越倾向于⾃我管理。在意识层面能够对⾃⼰所做的事情负起责任,同时积极思考应对各种变化(工作意愿⾛⾼。在实施层⾯上能够对⾃⼰的⼯作进⾏恰跟踪和总结。
  • 从管人的角度看,要保证开放轻松的氛围,尊重所有有观点的⼈人的意⻅见。要保证投⼊入产出的公平和公正。这⾥里的公平公正是指:
  • 对于任何一个成员也可以用一个简单的坐标系的四个象限来进⾏行划分,这个坐标系的横轴和纵轴分别是:能⼒力是否能胜任既定的⼯工作范畴;精神及价值取向是否与团队整体的价值取向相吻合。

对于一份代码其⾮功能性质(如:可维护性等)事实上是个有机体,需要持续维护。然⽽这部分质量并不能很好的量化(逻辑链6),也就并不能很好的对应于⼈件费⽤,这会导致启⽤外包时会出现有⼼的⽆⼒,有力的⽆⼼这样一种局⾯;发包的有⼼期望收获⾮功能性质量,但没⼒量去做;⽽接包的有⼒做,却没理由这样做。这必然会导致代码内在质量的下滑,但这是权利和责任分离的种必然结果,暂时还看不到彻底的解决⽅案。

在《软件架构设计》书中,温昱先⽣提出了5试图法,这可以让人比较清楚的一窥架构设计的概貌:

  • 逻辑架构 逻辑架构关注功能。不仅包括⽤户可⻅的功能,还包括为实现为⽤户功能⽽必须提供的“辅助功能模“:它们可能是逻辑层、功能模块和类等。
  • 开发架构 开发架构关注程序包,不仅包括要编写的源程序,还包括直接使⽤ݒ第三⽅SDK和现成框架、类库,以及开发的系统将运⾏于其上的系统软或中间件。
  • 运⾏架 运⾏架构关注进程线程、对象等运⾏时概念以及相关的并发、同步、通信等问题。
  • 物理架构 物理架构关注“目标程序及其依赖的运⾏库和系统软件”最终如或部署到物理机器,以及如何部署机器和⺴˷络来配合软件系统的可靠可伸缩性等要求。
  • 数据架构 数据架构关注持久化数据的存储⽅案,不仅包括实体及实体关系的数存储格式,还可能包括数据传递、数据复制和数据同步等策略。 --温昱,《软件架构设计》

这也就决定了所谓的设计和编码⽅法只有三个基本选项

  • 以逻辑关系为中⼼---这就是我们常说的结构化分析和设计
  • 以概念为中⼼---这就是我们常说的⾯ॶ向对象分析和设
  • 两者兼顾 ---这是一直被忽略的个视角,本书会在后⽂论及这种混合模式

    如果把视⾓再拔⾼点就会世界再怎么丰富多彩,但你描述它的时候,基本要素也只是:名词(主语)和动词(谓语)。
    我们分解世界中问题的时候,要么以名词为中⼼要么以动词为中⼼要么混合着来。⼏Ԟ千年累积的历史可为这观点的明
    在软件的世界中,以逻辑为中⼼⼤致等价于以动词为中以,概念为中⼼⼤价于以名词为中⼼。

在设计和编码过程中:设计可以⼤致等价于计(P),编码可以⼤致等于实施(D),测试可以⼤致等价于检(C),调试和修正可以⼤致等价分析(A)和改进(I)。

所有软件活动包括根本任务----打造构成抽象软件实体的复杂概念结构,次要任务----使用编程语言表达这些抽象实体,在空间和时间限制内将它们映射成机器语言。

在现实中软件是商业的延续,因此大多时候在软件开发中商业因素比技术因素重要。

们前⽂٘曾经做过这样的假设:
如果我们认为积极向上的团队⼯作意愿为1,能对⼯作基本负责的团队⼯作意愿为0.5,负不起责任的团队⼯作意愿为0.5以下,极值为0。那么糟糕的代码很可能使⼯作效能降低半。
所以说从⻓期来看,设计和编码的好坏⾄少可以导致4倍左右的效能差异。
这⾥需要特别提到的是对现有软件产品的选用,尤其是平台的选⽤。

当我们选择Windows或Linux,选择Java或.net,选择SQL或NoSQL等时,事实上也就选择了其背后所隐含的⼒量,这种⼒量最终将作⽤于组织⼒量O,决定基线的⾼度。假使说A技术代表的基线⾼度为20,B技术代表的基线⾼度为40,那么选择A的⼈或公司⽆无疑的天⽣生处于劣势。

庄⼦ֵ对上述现象进⾏过⾮常形象的描述,他说:⾃其异者视之,肝胆楚越也;⾃其同者视之,万物皆一也

我们可以根据肤⾊把⼈分为⻩⾊⼈种,⽩⾊⼈种,⿊⾊⼈种,但你不能根据⼈穿得⾐服把⼈分为⻩⾐服的⼈,⽩⾐服的⼈,⿊⾐服的⼈,后者定会导致杂多的概念。分类的基础应该是尽可能本质的,恒常的特质,同时尽量避免基于偶然的,短期的特质进⾏分类。前者有必要使⽤继承,后者大多时候则可以体现为属性。

以多线程和多进程⽽论,确定“什么时候适合使用这种技术”是⽐“怎么使⽤这项技术”困难的多的事情。

对于多线程的误⽤,在MSDN上可以找到份很好的总结,这⾥提取⼏个典型的:

  • 锁争⽤和顺序执⾏。这时虽然启⽤了多线程,但实际执⾏起来这些线程是串⾏的。
  • 过度订阅。如果系统的核数远少于线程数,同时启动很多个线程,时间往往会浪费在抢占上。
  • I/O效率低。多线程被⽤来做频繁的I/O操作,这会导致很多时间开销在I/O

并⾏的种类
为并⾏分类并不是很容易的事,这⾥ज只介绍种最简单的,依据内存架构对并⾏进⾏分类的⽅法。依据内存架构,并⾏可以分为三类:

  • Shared Memory: 常说的多线程即是这类,⽐ڽ较有名的实现是OpenMP。
  • Distributed Memory:如果多台PC组合起来进⾏并⾏计算那就是这类。⽐较有名的实现是MPI。
  • Hybrid Distributed-Shared Memory:如果多台PC进⾏分布,每台PC上还启⽤多核,那就是这类。

抽象数据类型(ADT:Abstract Data Type)
谈及封装的话,很多⼈⽴刻就会想到⾯向对象,但事实上封装的历史要⽐⾯向对象还要⻓些。从抽象数据类型(ADT)开始,⼈们已经开始使⽤封装这思想。
抽象数据类型(ADT)更像是类的前⾝,本⾝并不是复杂的概念。当你有组数据以及与其捆绑在起的组操作,你就有了个抽象数据类型(ADT)。

《代码⼤全》中列了些典型的抽象数据类型,如:

  • 油罐
    • 填充油罐
    • 排空油罐
    • 获取油罐容积
    • 获取油罐状态
  • 列表
    • 初始化列表
    • 向列表中插⼊Ԗ条目
    • 从列表中删除条目
    • 读取列表中的下个条目

如果概念之间是正交的,概念的边界也是清晰的,那么命名⽆疑会容易很多。好的设计是改善命名的基础。也就是说,很多时候我们感到命名困难,真正原因可能并⾮是命名的技能不够,⽽是设计还不够优化。
在努⼒改善设计之后,才需要⾯对纯粹的命名问题。
从本质上来看,命名问题并不是个编程的问题,⽽是个表达的问题。命名最终对读程序的⼈负责

探讨什么时候可以结束设计时,需要假设输⼊的需求是正确的。在此前提下,⽤不上的设计⼤致可以分为两类:
一是程度问题。设计如果关注某些细节,⽽这些细节⾃⾝却随着认知的深⼊⽽发⽣变化,那么与此相关的设计会变成⽆⽤的。
是⽆意间扩散了需求的边界。⽐如:在看不到可移植性需求的情形下,为可移植性做了准备。这类设计的⽆⽤并不体现在技术上,⽽是体现在成本上。

假使说设计上考虑了个因素,其所需要的开销是X,⽽不考虑这要素,在后期发现并进⾏对应,其对应成本是Y,那么凡是会导致X<Y的东⻄就都是需要在设计中预先澄清,⽽不能等待在编码中逐步澄清的

软件设计⾃⾝最优化与项目本⾝资源有限、时间有限间的⽭盾。

总结来看,在寻找最佳解决问题⽅法时,完美的设计与编码有六个关键原则需要遵守:

  • 正交的概念和逻辑
  • 层次的最⼩化
  • 时序清晰
  • 隐藏不必要公开的信息
  • 名实相符

  • 从分⼯的⾓度看,凡是涉及两⼈以上的东⻄(接⼝,数据等)X<Y的可能性都极⼤。因为这部分⼯作一旦做了,并且是正确的,那节约的时间往往是要以倍计的。所以设计的终⽌尺度可以是每个⼈和别⼈间的关联得到澄清。

  • 从⽀撑需求的⾓度看,那么则要检查是否可以⽀撑体现软件核⼼价值的需求。尤其是⾮功能性的需求。这类需求甚⾄有导致既有实现推倒重来的可能性,⻛险太⾼,定要事先考虑。⽐如:是否跨平台等。

  • 从⼈员状况来看,能⼒差的⼈越多,设计的程度应该越细,反之则可以使⾃主程度⾼点。因为能⼒差的⼈⾃⾝所做的决策,错的可能性较⾼,即Y偏⼤。

⾯向对象设计原则和上述关键点的关系
在《敏捷软件开发中:原则,模式和实践》书中,作者共提及了11条⾯向对象的设计原则。他们分别是:

  • SRP 单职责原则:就一个类⽽⾔,应该仅有个引起它变化的原因
  • OCP 开放-封闭原则:软件实体(类,模块,函数等)应该是可以扩展的,但是不可修改
  • LSP Liskov替换原则:⼦类型必须能够替换掉它们的基类型 - DIP 依赖倒置原则:抽象不依赖于细节。细节应该依赖于抽象。
  • ISP 接⼝隔离原则:不应该强迫客户依赖于他们不⽤的⽅法,接⼝属于客户,不属于他所在的层次结构。
  • REP 重⽤用发布等价原则:重⽤用的粒度就是发布的粒度。
  • CCP 共同封闭原则:包中的所有类对于同⼀一类性质的变化应该是共同
  • 封闭的。一个变化若对一个包产⽣生影响,则将对该包中的所有类产⽣生影响,⽽而对于其他的包不造成任何影响。
  • CRP 共同重⽤用原则:⼀一个包中的所有类应该是共同重⽤用的。如果重⽤用了包中的⼀一个类,那么就要重⽤用包中所有的类。
  • ADP ⽆无环依赖原则: 在包得依赖关系图中不允许存在环。
  • SDP 稳定依赖原则:朝着稳定的⽅方向进⾏行依赖。
  • SAP 稳定抽象原则:包得抽象程度应该和其稳定程度一致。

计编码有问题的典型症状是:

  • 程序员普遍焦虑。一旦改了点代码,很难准确知道会有什么后果。
  • 没⼈敢改代码,也没⼈愿意改代码。
  • 不太可能写出真的UT。 新⼈介⼊成本极⼤,往往需要了解整个程序。
  • 功能⽆法拆装拼接