登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

秒大刀 博客

好好学习 天天向上

 
 
 

日志

 
 
 
 

读《代码大全2》第2部分 创建高质量的代码  

2009-11-13 18:17:27|  分类: 读书 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
全部书摘参见 读《代码大全2》


第2部分 创建高质量的代码

第5章 软件构建中的设计
       如何管理复杂度:
              把任何人在同一时间需要处理的本质复杂度的量减到最小
              不要让偶然复杂度无谓的快速增长
       理想的设计特征:
              最小的复杂度:“聪明的”设计往往都是难以理解的。如果你的设计方案不能让你在专注于程序的一部分时安心的忽视其他部分的话,就不是成功的设计
              易于维护:把做维护的程序员做为你的听众,进而设计出能自明的系统
              松散耦合:让程序的各个组成部分之间的关系最小
              可扩展性:能增强系统的功能而无须破坏其底层结构
              可重用性:系统的组成部分能在其他系统中重用
              高扇入:让大量的类使用某个给定的类
              低扇出:一个类里少量或适中的使用其他的类(最好不要超过7个)
              可移植性:
              精简性:系统没有多余的部分。伏尔泰:“一本书的完成,不在它能不能再加入任何内容的时候,而在不能再删去任何内容的时候”。每一行代码都是成本。
              层次性:能在任意的层面上观察系统,并得到某种具有一致性的看法。系统应该能在任意层次上观察而不需要进入其他层次。
              标准技术:系统所依赖的外来的、古怪的东西越多,别人想要第一次理解它的时候就越头疼。要尽量用标准的、常用的方法,给人一种熟悉的感觉。
       设计的层次:
              软件系统
              子系统和包:应限制不同子系统间的通信来让每个系统都有存在的意义
              包中的类
              类中的数据和子程序
              子程序内部
       抽象是一种能让你在关注某一概念的同时可以放心的忽略其中一些细节的能力——在不同的层次处理不同的细节
       抽象:可以让你从高层的细节来看待一个对象;封装:除此之外,你不能看到对象的任何其他细节层次
       信息隐藏:
              隐藏复杂度:这样你就不用再去应付它,除非你要特别关注的时候
              隐藏变化源:这样当变化发生时,其影响就能被限制在局部范围内
       大多数让信息无法隐藏的障碍都是由于惯用某些技术而导致的心理障碍
       设计模式提供的益处:
              设计模式通过提供现成的抽象来减少复杂度
              设计模式通过把常见解决方案的细节予以制度化来减少出错
              设计模式通过提供多种设计方案而带来启发性的价值
              设计模式通过把设计对话提升到一个更高的层次上来简化交流
常见的设计模式:
       Abstract Factory: 通过指定对象组的种类而非单个对象的类型来支持创建一组相关的对象
       Adapter:把一个类的接口转变为另一个接口
       Bridge:把接口和实现分类开来,使它们可以独立的变化
       Composite:创建一个包含其他同类对象的对象,使得客户代码可以与最上层对象交互而无须考虑所有的细节对象
       Decrorator:给一个对象动态的添加职责,而无须为了每一种可能的职责配置情况去创建特定的子类
       Fa?ade:为没有提供一致接口的代码提供一个一致的接口
       Factory Method:做特定基类的派生类的实例化,除了在Factory Method内部之外均无需了解各派生对象的具体类型
       Iterator:提供一个服务对象来顺序的访问一组元素中的各个元素
       Observer:使一组相关对象同步,方法是让另一个对象负责:在这组对象中的任何一个发生变化时,由它把这种变化通知给这个组里的所有对象
       Singleton:为有且仅有一个实例的类提供一种全局访问功能
       Stratery:定义一组算法或行为,使得他们可以动态的相互替换
       Template Method:定义一个操作的算法结构,但是把部分实现的细节留给子类
       不能去强迫代码去适应某个模式,不能“为了模式而模式”
       内聚——类内部的子程序或者子程序内的所有代码在支持一个中心目标上的紧密程度。内聚性是用来管理复杂度的有力工具。
  分层是管理复杂度的另一个有力工具。
  分层结构是实现软件的首要技术使用的有用工具,它能使你只关注于当前正在关注的那一层细节。
  为测试而设计通常能产生更为规整的类接口
设计中的启发式方法:
       寻找现实中的对象
       形成一致的抽象
       封装实现细节
       在可能的情况下继承
       藏住秘密(信息隐藏)
       找出容易改变的区域
       保持松散耦合
       探寻通用的设计模式
       高内聚性
       构造分层结构
       严格描述类契约
       分配职责
       为测试而设计
       避免失误
       有意识的选择绑定时间
       创建中央控制点
       考虑使用蛮力
       画一个图
       保持设计模块化
  最有效的原则之一就是不要卡在单一的方法上
  如果目前的解决方案对你来说都有些棘手,那么日后他人再去维护时肯定更感觉到困难重重
  除非你完全的解决了某一设计问题,否则你无法完整的定义出该设计问题
  原型代码本来就是写来为扔掉的,所以应该用最少的代码和最简单的做法去快速完成原型设计
核对表:软件构造中的设计
       设计实践:
       你已经做过多次迭代,并且从众多尝试结果中选择最佳的一种,而不是简单选择第一次尝试的结果?
       你尝试用多种方案来分析系统,以确定最佳方案吗?
       你同时用自上而下和自下而上的方法来解决设计问题吗?
       为了解决某些特定问题,你对系统中的风险部分或者不熟悉的部分创建过原型、写出数量最少的可抛弃代码吗?
       你的设计被其他人检查了吗?(无论正式与否)
       你一直在展开设计,直到实施细节跃然纸上了吗?
       你用某种适当的技术——比如wiki、电子邮件、挂图、数码相机、UML或者在代码里写注释——来保留设计成果吗?
       设计目标:
       你的设计是否充分的处理了由系统架构层定义出并且推迟确定的事项?
       你的设计被划分为层次吗?
       你对把这一程序分解成为子程序、包和类的方法感到满意吗?
       你对把这个类分解成为子程序的方法感到满意吗?
       类与类之间的交互关系是否已设计为最小化?
       类和子程序是否被设计为能够在其他的系统中重用?
       程序是不是易于维护?
       设计是否精简?设计出来的每一部分都绝对必要吗?
       设计中是否采用了标准技术?是否避免使用怪异且难以理解的元素?
       整体而言,你的设计是否有助于最小化偶然性的和本质的复杂度吗?
  软件的首要技术使命就是管理复杂度。以简单性作为努力目标的设计方案对此最有帮助
  简单性可以通过两种方法来获取:一是减少在同一时间所关注的本质性复杂度的量,二是避免生成不必要的偶然复杂度。
  设计是一种启发式的过程。固执于某一种单一方法会损害创新能力,从而损害你的程序
  好的设计都是迭代的。你尝试设计的可能性越多,你最终解决方案就会变得越好
  信息隐藏是个非常有价值的概念。通过询问“我应该隐藏些什么?”能够解决很多困难的设计问题

第6章 可以工作的类
  1.  成为高效程序员的一个关键就是在于,当你开发程序任一部分的代码时,都能安全地忽视程序中尽可能多的其余部分。
  2. 使用ADT的益处:
    1. 可以隐藏实现细节
    2. 改动不会影响到整个程序
    3. 让接口能提供更多信息
    4. 更容易提高性能
    5. 让程序的正确性更显而易见
    6. 程序更具自我说明性
    7. 无须在程序内到处传递数据
    8. 你可以像在现实世界中那样操作实体,而不用在底层实现上操作它
  3. 创建ADT的建议:
    1. 把常见的底层数据类型创建为ADT并使用ADT,而不再使用底层数据类型
    2. 把像文件这样的常见对象当成ADT
    3. 简单的事物也可当做ADT
    4. 不要让ADT依赖于其存贮介质
  4. 类可以看做是ADT+多态+继承
  5. 创建类接口的建议:
    1. 类的接口应该展现一致的抽象层次
    2. 一定要理解类所实现的抽象是什么
    3. 提供成对的服务
    4. 把不相关的信息转移到其他类中
    5. 尽可能让接口可编程,而不是表达语义
      1. 一个接口中任何无法通过编译器强制实施的部分,就是一个可能被无用的部分
    6. 谨慎在修改时破坏接口的抽象
    7. 不要添加与接口抽象不一致的公用成员
    8. 同时考虑抽象性和内聚性
  6. 良好的封装:
    1. 尽可能的限制类和成员的可访问性
    2. 不要公开暴露成员数据
    3. 避免把私用的实现字节放入类的接口中
    4. 不要对类的使用者做出任何假设
    5. 避免使用友元类
    6. 不要因为一个子程序里仅使用公用子程序,就把它归入公开接口
    7. 让阅读代码比编写代码更方便
      1. 阅读代码的次数比编写代码的次数多得多,即使在开发初期也是如此
    8. 要格外警惕从语义上破坏封装性
    9. 留意过于紧密的耦合关系
      1. 避免使用友元,因为它们之间是紧耦合的
      2. 在基类中把数据声明为private而不是protected,以降低派生类和基类之间的耦合程度
  7. has a:
    1. 通过包含类实现has a关系
    2. 在万不得已时通过private继承来实现has a关系
    3. 警惕有超过约7个数据成员的类
      1. 人类能记住的离散项目的个数是7±2
  8. is a:
    1. 用public继承来实现is a关系
    2. 要么使用继承并进行详细说明,要么就不要使用它
      1. 继承给程序增加了复杂度,是一种危险的技术
    3. 遵循Liskov替换原则
      1. 派生类必须能通过积累的接口而被使用,且使用者无须了解两者之间的差异
    4. 确保之继承需要继承的部分
      1. 如果你只是想使用一个类的实现而不是接口,请选择包含而不是继承
    5. 不要“覆盖”一个不可覆盖的成员函数
      1. 派生类中的成员函数尽量不要与基类中的相同
    6. 把公用的接口、数据及操作放到继承树种尽可能高的位置
    7. 只有一个实例的类是值得怀疑的
      1. 可能意味着:将类和对象混为一谈
      2. Singleton是特例
    8. 只有一个派生类的基类也值得怀疑
      1. 可能意味着:提前设计
      2. 不要创建任何并非绝对必要的继承结构
    9. 派生后覆盖了某个子程序,但在其中没做任何操作的情况也值得怀疑
    10. 避免让继承体系过深
      1. 大多数人在脑中同时应付超过2到3层的继承时就感觉到吃力了
    11. 尽量使用多态,避免大量的类型检查
    12. 让所有数据都是private,而非protected
  9. 继承和包含的决定:
    1. 如果多个类共享数据而非行为,应该创建这些类可以包含的共用对象
    2. 如果多个类共享行为而非数据,应该让他们从共同的基类继承而来,并在基类里定义共用的子程序
    3. 如果多个类既共享数据也共享行为,应该让他们从共同的基类继承而来,并在基类里定义共用的子程序
    4. 当你想由基类控制接口时,使用继承;当你想自己控制接口时,使用包含
  10. 成员函数和数据成员建议:
    1. 让类中子程序的数量尽可能的少
    2. 禁止隐式的产生你不需要的成员函数和运算符
    3. 减少类所调用的不同子程序的数量
    4. 对其他类的子程序间接调用要尽可能少
    5. 应尽量减少类和类之间相互合作的范围
  11. 构造函数建议:
    1. 应该在所有的构造函数中初始化所有的数据成员
      1. 属于防御式编程实践
    2. 用private构造函数来强制实现单件属性
    3. 优先采用深拷贝,除非论证可行才采用浅拷贝
      1. 采用浅拷贝一般是基于性能考虑的,但为了不确定的性能提高而增加复杂度是不妥的
  12. 创建类的原因:
    1. 为现实世界中的对象建模
    2. 为抽象的对象建模
      1. 得出恰当的抽象对象是OOD中一项主要挑战
    3. 降低复杂度
    4. 隔离复杂度
    5. 隐藏实现细节
    6. 限制变动的影响范围
    7. 隐藏全局数据
    8. 让参数传递更顺畅
    9. 建立中心控制点
    10. 让代码更易于重用
    11. 为程序族做计划
    12. 把相关的操作包装到一起
    13. 实现某种特定的重构
  13. 应该避免创建的类:
    1. 避免创建万能类
    2. 消除无关紧要的类
    3. 避免用动词命名的类
  14. 不同语言在“类”方面的差异:
    1. 在继承层次中被覆盖的构造函数和析构函数的行为
    2. 在异常处理时构造函数和析构函数的行为
    3. 默认无参构造函数的重要性
    4. 析构函数或Finalizer终结器的调用时机
    5. 和覆盖语言内置的运算符(赋值和等号)相关的知识
    6. 当对象被创建和销毁时,或当其被声明时,或者它所在的作用域退出时,处理内存的方式
  15. 类是当前程序员实现模块化的最佳方式
  16. Key Points:
    1. 类的接口应提供一致的抽象。很多问题都是由于违背该原则而引起的
    2. 类的接口应隐藏一些信息——如某个系统接口、某项设计决策、或一些实现细节
    3. 包含往往比继承更为可取——除非你要对“is a”的关系建模
    4. 继承是一种有用的工具,但它却会增加复杂度,这有违于软件的首要技术使命——管理复杂度
    5. 类是管理复杂度的首选工具。要在设计类时给予足够的关注才能实现该目标
 
第7章 高质量的子程序
  1.  创建子程序的正当理由: 
    1. 降低复杂度。如果没有子程序的抽象能力,我们的智力将根本无法管理复杂的程序
    2. 引入中间、易懂的抽象。将一段晦涩的代码组织成名称明显的子程序
    3. 避免代码重复
    4. 支持子类化覆盖
    5. 隐藏顺序
    6. 隐藏指针操作。指针操作的可读性通常都很差,而且也很容易出错
    7. 提高可移植性
    8. 简化复杂的布尔判断
    9. 改善性能
    10. 确保子程序都很小。不绝对,有时写一个大的子程序会更好
  2. 理解一些概念要比记住一些特定的术语更重要
  3. 功能内聚性:让一个子程序仅执行一项操作
  4. 好的子程序名字:
    1. 描述子程序所做的所有事情
    2. 避免使用无意义的、模糊或者表述不清的动词
    3. 不要仅通过数字来形成不同的子程序名字
    4. 根据需要确定子程序名字的长度。变量名的最佳长度是9到15个字符
    5. 给函数命名时要对返回值有所描述
    6. 给过程起名时使用语气强烈的动词加宾语形式。OO语言中无需在过程名字加入对象的名字
    7. 准确使用对仗词
    8. 为常用操作确立命名规则
  5. 尽量不要让子程序超过200行,50到150行是较好的选择
  6. Key Points:
    1. 创建子程序最主要的目的是提高程序的可管理性,当然也有其他一些好的理由。其中,节省代码空间只是一个次要原因;提高可读性、可靠性和可修改性等原因更重要一些
    2. 有时候,把一些简单的操作写成独立的子程序也非常有价值
    3. 子程序可以按照其内聚性非为很多类,而你应该让大多数子程序具有功能上的内聚性,这是最佳的一种内聚性
    4. 子程序的名字是它质量的指示器。如果名字糟糕但恰如其分,那就说明这个子程序设计的很差劲。如果名字糟糕而且又不准确,那么它就反映不出程序是干什么的。不管怎样,糟糕的名字都意味着程序需要修改
    5. 只有在某个子程序的主要目的是返回其名字所描述的特定结果时,才应该使用函数。(有些语言把无返回值的子程序叫“过程”,有返回值的才叫“函数”)
    6. 细心的程序员会非常谨慎的使用宏,而且只在万不得已时才用。

第8章 防御式编程
  1. 防御式编程主要思想:子程序应该不因传入错误数据而被破坏,哪怕是由其他子程序产生的错误数据
  2. 使用断言的指导建议:
    1. 用错误处理代码来处理预期会发生的情况,用断言处理绝不应该发生的状况
    2. 避免把需要执行的代码放到断言中
    3. 用断言来注解并验证前条件和候条件——契约式设计
    4. 对于高健壮性的代码,应该先使用断言再处理错误
  3. 异常的应用情形和断言相似——都是用来处理那些不仅罕见甚至永远都不该发生的情况
  4. 异常实际上在设计上破坏了封装性
  5. Key Points:
    1. 最终产品代码中对错误的处理方式要比“垃圾进,垃圾出”复杂得多
    2. 防御式编程技术可以让错误更容易发现、更容易修改,并减少错误对产品代码的破坏
    3. 断言可以帮助人尽早发现错误,尤其是在大型系统多和高可靠性的系统中,以及快速变化的代码中
    4. 关于如何处理错误输入的决策是一项关键的错误处理决策,也是一项关键的高层设计决策
    5. 异常提供了一种与代码正常流程角度不同的错误处理手段。如果留心使用异常,它可以成为程序员们知识工具箱中的一项有益补充,同时也应该在异常和其他错误处理手段之间进行权衡比较。
    6. 针对产品代码的限制并不适用于开发中的软件。你可以利用这一优势在开发中添加有助于更快的排查错误的代码

第9章 伪代码编程过程
  1. 最主要的优化还是在于完善高层的设计,而不是完善每个子程序
  2. Key Points:
    1. 创建类和子程序通常都是一个迭代的过程。在创建子程序的过程中获得的认识常常会反过来影响类的设计
    2. 编写好的伪代码需要使用易懂的英语,要避免使用特定编程语言中才有的特性,同时要在意图的层面上写代码,描述该做什么,而不是要怎么去做
    3. 伪代码编程过程是一个性质有效的做详细设计的工具,它同时让编码工作更容易。伪代码会直接转化为注释,从而确保了注释的准确度和实用性。
    4. 不要只停留在你想到的第一个设计方案上。反复使用伪代码做出多种方案,然后选出其中最佳的一种方案再开始编码
    5. 每一步完成后都要检查你的工作成果,还要鼓励其他人帮你来检查。这样你就会在投入精力最好的时候,以最低的成本发现错误。
  评论这张
 
阅读(1835)| 评论(0)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018