• 2009-08-13

    《程序员修炼之道》 第二章 注重实效的途径 - [新知随笔]

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://liuyangsl.blogbus.com/logs/44090177.html

    1. 重复的危害

    软件系统中所使用的知识,是会不稳定变化的。比如市场环境的改变,政府决策的变化。
    当知识变化的时候,软件系统的基础就出现了错误,这时就需要维护。
    维护不应该只存在于软件的发布阶段,而是应该贯穿于开发过程的始终。

    如果系统中对同一个知识进行了重复,那将给维护带来无法估计的麻烦。
    因此应该避免在多个地方表达同一事物,这就是DRY原则(Don't repeat yourself)。(不管是代码还是文档)

    重复的出现有以下几种:

    (1)强加的重复:
    因为代码结构的约束而不得不出现的重复,有以下几种。

    <a>信息的多种表示:
    跨越不同子系统使用同一知识结构。可以使用一个生成器,在多种语言的环境下,利用数据库中的元数据来构造出符合条件的结构体。
    <b>代码中的注释:
    注释应存在于高级结构中,以防止在低级应用中出现注释重复。
    <c>文档与代码:
    当代码改变后,文档往往没有时间更新。不妨用文档来驱动代码,先更新文档,再根据文档来操作代码。
    <d>语言问题:
    尤其是在面向对象语言中,会强加很多重复,比如接口及其实现,函数重构等。这种情况只能在编译时,仔细考察头文件和实现文件之间的差异。

    (2)无意的重复:
    在设计中,由于对象设计不合理,会产生重复:

    <a>不同对象类型中,出现重复的属性。(比如:{a,b,c} {a,d,e})
    <b>一个对象中,一个属性的改变会使另一个属性也需要改变。(比如:{start,end,length})

    对象中的属性,应该使用访问器(get、set)来操作。

    (3)无耐性的重复:
    开发人员为了节省开发时间,产生的重复。但是往往欲速而不达。

    (4)开发者之间的重复:
    在开发人员开发自己的独立模块时,很可能生产出一些重复的基本逻辑处理函数。
    应鼓励开发者互相进行主动的交流。比如新闻组和论坛。
    集中存放实用例程和角本。以便找到可复用的东西。
    阅读他人代码和文档,互助复查。

    ---------------------------------------------------------------------------------

    2. 正交性

    两个直线相互垂直成为正交。在坐标上表现为,当X值变化时,Y值不变。
    也就是说两个或多个事物中一个变化,不会影响到其他。(不依赖性、解耦性)

    正交系统的设计就是消除无关事物之间的影响。设计独立、自足的组件。
    正交可以提高生产效率、降低风险、便于测试和修改Bug。

    团队任务的划分也应该注意正交性。
    将基础设施与应用分离。
    先按照基础组件(数据库、通信接口、中间件、等)划分子团队。
    再根据应用功能和成员能力进行调整,尽量降低子团队之间的偶合性。

    设计正交系统:
    系统由一系列相互协作的模块组成,每个模块实现不依赖于其他模块的功能。
    模块可以被分为很多层,每层提供一级抽象,每层只使用下面一层的抽象。
    当一个模块的功能变化时,只需要调整这个模块和它下一层的接口即可。

    “对象持久模型”(object persistence scheme)
    正交性也体现在:面向方面编程(AOP)

    在使用外部工具或库的时候,也应该注意正交,这样可以很容易地更换提供商。
    文档也可以使用正交。

    正交编码
    <a> 让代码保持解偶:除非必须,不要向外部模块暴露任何事情,也不去依赖外部模块。
    <b> 避免使用全局数据。应该把需要的东西显式地传入模块而不是在模块中去访问共享。
    <c> 避免编写相似的函数。

    ---------------------------------------------------------------------------------

    3. 可撤销性

    不要过于依赖某一个事实,以至于当它改变时已经没有其他的解决办法。
    我们在项目初期会做出一些重要决策,随着项目的进展,可选择的余地会越来越小,这些决策变得不能逆转。

    一些方法可以帮助我们避免做出不可逆转的决策,DRY原则、解耦、使用元数据等等。
    最重要的就是充分预计决策的可能变化,使项目保持灵活。

    灵活性:代码、架构、部署、供应商集成。

    在设计系统的时候应该注意:
    <a> 考虑到单机结构、C-S结构、n层结构的简单转换。
    <b> 应该把第三方产品隐藏在定义良好的接口后面。
    <c> 注意系统兼容性。

    ---------------------------------------------------------------------------------

    4. 曳光弹

    在夜间战争中,射击往往使用曳光弹和子弹交错的形式,一发曳光弹一发子弹。如果明亮的曳光弹打中了目标,那么子弹也就会打中。这种反馈很即时,因为曳光弹处于和子弹同样的环境中,降低了外部影响,并且非常快速地接近目标。

    当要构建一个从没有构建过的东西时,基本上已知的东西很少。含糊不清的需求、不熟悉的算法、技术、语言、库。这时候我们需要能在黑暗中发光的代码。

    曳光代码是简单功能的试探,通过实现一个和项目相似的简单功能,就可以快速地判断可行性、找出困难。
    之后,曳光代码将被扩展、完善,做为最终项目的一部分。
    因此,曳光代码需要完整的错误检查、结构、文档、自查。

    曳光代码的优点:
    <a> 用户可以及早看到实例,并通过直观的对比来纠正需求上的错误。(重新构造曳光代码)
    <b> 开发者构建出一个可行的工作结构。
    <c> 以曳光代码做为平台,每天的工作结果都可以集成上去,成为一个可用的集成测试原型。
    <d> 可以演示。
    <e> 非常直观的工作进展情况。

    曳光代码不同于原型。
    原型可以是一个片面功能的示意,比如用户界面演示,而曳光代码则是一个可用的简单整体架构。
    原型制作应该放在曳光代码之前,做为对目标侦察和试探。

    ---------------------------------------------------------------------------------

    5. 原型与便签

    原型是为了分析和揭示风险。
    原型可以使多种形式的,只要能够准确地表达开发者的理解,并回答一些问题。

    对以下工作建议制作原型:
    架构、已有系统中的新功能、外部数据的结构或内容、第三方工具组件、性能、用户界面。

    原型的价值在于通过它获得的经验教训。
    原型应该尽量掩盖细节,把注意力集中在希望获得解答的问题上。

    从原型中寻求解答
    主要组件的责任以及主要组件之间的协作关系。
    耦合最小化。估计重复的潜在来源。
    接口定义、约束关系、数据访问。

    原型往往是用过就扔的,并不完整,也不代表任何工作进度。
    但原型很容易被人误解,以为工作已经进行了很大一部分,如果需要避免这种误解,最好使用曳光代码。

    ---------------------------------------------------------------------------------

    6. 领域语言

    对于一个业务,可以用文字描述。可以用编程语言来实现。也可以定义小型的领域语言并编写解释器,制作专用的代码。

    一个项目中会涉及到不同领域,使用不同的领域语言来描述项目,有助于专心解决该领域中的问题,忽略琐碎的细节。

    数据语言:
    产生某种数据格式供程序使用。比如软件配置文件中特殊的数据格式,只有软件自己定义的解释器才能将其编译为可用的数据结构。

    命令语言:
    自定义一些命令语句,并构造解释器。比如脚本就是一种命令语言,可以直接执行。

    嵌入式语言:
    把高级命令语言直接嵌入到程序中。在程序运行的时候,加载不同的外部脚本就可以使程序进行不同的行为,不用重新编译,方便灵活。

    这些领域语言的文法大多简单易懂,但真的做到能够运行,却需要花很大力气来做解释的工作。
    但是这种努力可以使高级语言更易懂,易于维护,为后期开发带来越来越多的回报。

    ---------------------------------------------------------------------------------

    7. 估算

    估算是为了避免发生意外。

    估算的准确程度:不同的人对估算结果的准确性的要求有所不同。

    估算的方法:
    <a> 基本方法:去询问已经做过这件事的人。
    <b> 理解提问内容:想要了解问题的范围,再去进行思考。
    <c> 建立系统模型:需要一些源数据做为前提,开发一种普遍适用的计算方法。
    <d> 把模型分解为组件:考察组件之间的关系,和它们对结果的影响。
    <e> 给每个参数制定值:关注于那些对结构影响大的参数,保证尽量正确。
    <f> 计算结果:通过调整参数,考量结果是否符合期望。
    <g> 用实际结果来验证并改进估算方法。

    估算项目进度:
    在项目的开始,估算是模糊的。
    在开发进行了一部分之后,通过这部分工作的实际情况来进行重新估计。
    随着项目的进行,在多次迭代估算之后,估算的结果将趋向于正确。

    在被要求估算时,告诉他:“我等会儿回答你。”多花一点时间来思考细节,会得到更好的结果。

     


    收藏到:Del.icio.us