【读书笔记】代码大全

1.软件构建即编程,主要包括编码与调试、详细设计、规划构建、单元测试、集成、集成测试。构建是软件开发的核心活动,主要精力集中之处,产物是源代码。

2.软件的隐喻——建筑

3.三思而后行:前期准备(设计蓝图和建筑许可证在软件业的等价物,包括问题/产品定义、需求分析、架构)——目的:尽早发现并处理问题,识别并降低风险

3.1 前期准备的重要性:类比

在一个被污染了的环境中,水虱在受到核沾染的水中游泳,鲫鱼体内积聚了滴滴涕,而沙丁鱼生活的水域又遭受了石油污染,那么,不幸的海鸥由于处在食物链的最后一环,因此,它吃的不仅仅是沙丁鱼体内的石油,还有鲜鱼体内的滴滴涕和水虱体内的核废料。在程序设计中,如果需求定义遭受了污染,那么这又会影响结构设计,而这将最终影响创建活动。这将导致程序员们脾气暴躁而营养不良,同时生产出遭受严重污染而充满缺陷的软件。

4.关键的“构建”决策——程序员决定开发工具:语言、编程规范约定、统一风格、选择构建实践方法(如结对、TDD)

4.1 选择编程语言:Sapir-Whorf假说,你的思考能力取决于你的词汇量。因此编程语言会影响程序员的思维。

4.3 编程工具/语言不应该决定你的编程思路。“在一种语言上编程”的程序员将他们的思想限制于“语言直接支持的那些构件”。如果语言是初级的,那么程序员的思想也是初级的。“深入一种语言去编程”的程序员首先决定他要表达的思想是什么,然后决定如何使用特定语言提供的工具来表达这些思想。

5.软件构建中的设计

5.1.设计中的挑战:设计是自然而然形成的,设计不是从谁都脑袋里直接跳出来的,它是在不断的设计评估、非正式讨论、写试验代码以及修改试验代码中演化和完善的。

5.2.关键的设计概念:

管理复杂度:软件的首要技术使命便是管理复杂度,它实在是太重要了。当没人知道对一处代码的改动会对其他代码带来什么影响时,系统也就快停止发展了。降低复杂度的方法:把系统分解为多个子系统、保持子程序的短小精悍、从问题的领域着手而不是从底层实现细节入手、在最抽象的层次上工作。这些都能减少你的脑力负担。

设计的层次:识别出所有主要子系统,并定义允许各子系统如何使用其他子系统。子系统之间不能存在任何循环依赖。常用的子系统有:业务规则子系统、数据访问子系统、系统依赖子系统、用户界面子系统等

5.3.设计构造块:启发式方法

使用信息隐藏的原则来指导设计,类的接口应该尽可能少的暴露其内部工作机制,在做任何层面的设计时都要多问“该隐藏些什么?”来促成良好的设计决策。养成问“我该隐藏些什么?”的习惯,你会发现,很多棘手的问题都会迎刃而解。

找出易变区域,将其隐藏在不易变的接口后面。一些易变区域:业务规则、硬件依赖、输入输出、非标准的语言特性、丑陋的设计模块、状态变量、数据量的限制。不要把精力过多的浪费在那些不可能发生而又很难做出计划的变化上,应在那些变化特别明显而又容易做出计划的变化上多下功夫。

保持松散耦合,确保模块之间的连接关系尽可能的简单,使用参数连接而不是全局变量,一个模块越容易被其他模块调用,它们之间的耦合关系就越松散,就越灵活。最危险的耦合关系是模块之间的语义上的耦合关系,表现在使用控制标志、全局变量、方法调用顺序的依赖、强制类型转换等。

把设计看成是一个险恶的、扎乱的和启发式的过程。不要停留于你所想到的第一套解决方案,而是去寻求合作,跟同事讨论设计想法,征求意见,探求简洁性,在需要的时候做出原型,迭代,用各种非正式的方式记录各种设计方案。最终获得满意的设计方案。

5.4 设计实践:迭代、分而治之、自上而下、自下而上、原型、合作设计、记录设计成果

6.可以工作的类

以类为基础思考编程问题,成为高效程序员的一个关键就在于,当你开发程序任一部分代码时,都能安全的忽视程序中尽可能多的其余部分。而类就是实现这一目标的首要工具。


6.1 类的基础:抽象数据类型ADT
要理解面向对象编程,首要理解ADT。ADT让你像在现实世界中一样操作实体,而不必在底层的实现上摆弄实体。ADT——封装,隐藏细节
类=ADT+继承+多态

6.2 良好的类接口
类所有的公用(public)子程序构成的集合,就是类的接口。
通过接口来展现合理的抽象,清晰的接口,让人一眼就知道这个类能干什么。

优秀的抽象接口具有的特点:
  • 展现一致的抽象层次(即同一接口,展现的抽象层次不能高低不一或抽象和细节同时展现)。每个类应该只实现一个ADT。
  • 考虑提供成对的服务:开/关、增/删、enable/disable等。
  • 把不相关的信息转移到其他类
  • 一定要理解类所实现的抽象是什么,你需要的抽象是什么
  • 尽可能让接口可编程,而不是表达语义。接口调用尽可能不要依赖逻辑约定。
  • 谨防在修改时破坏接口的抽象
  • 不要添加与现有接口抽象不一致的公用接口
  • 同时考虑抽象性和内聚性

什么是良好的封装:
  • 尽可能的限制类和成员的可访问性,在引入公共方法时,先考虑是否破坏了类接口抽象的完整性。
  • 不要公开暴露数据成员。
  • 避免把私有的实现细节放入类的接口中
  • 不要让类的使用者做出任何假设
  • 格外警惕从语义上破坏封装性(如果调用方代码不是依赖于类的公共接口,而是依赖于类的私有实现,即不是针对接口编程,而是透过接口针对内部实现编程时,就是在语义上破坏了封装性)
  • 保持类之间的松耦合
封装和抽象的关系:
抽象通过提供一个可以让你忽略细节的模型来管理复杂度,而封装则强制阻止你看到细节。没有好的封装,抽象往往很容易被打破。抽象和封装是共生共灭的。良好的封装是良好的抽象的基础。

6.3 有关设计和实现的问题
1.继承的使用原则:
  • 要么使用继承并进行详细说明,要么就不要用它
  • 遵循LSP
  • 把共用的接口、数据及操作放到继承树中尽可能高的位置
  • 只有一个派生类的基类值得怀疑——提前设计了(为未来要做的工作进行准备的最好方法,不是去创建几层额外的、没准以后哪天能用上的基类,而是让眼下的工作尽可能的清晰、简单、直截了当。也就是说,不要创建任何并非绝对必要的继承结构)
  • 覆盖后的方法若没任何操作也值得怀疑
  • 避免继承体系过深(2到3层即可)
  • 让所有数据都是private的,如果派生类需要访问基类的数据,则提供protected的访问函数
2.何时使用继承,何时使用包含
  • 如果多个类共享数据而非行为,应该创建这些类可以包含的共用对象
  • 如果多个类共享行为而非数据,应该让他们从共同的基类继承而来,并在基类里定义共用的子程序
  • 如果多个类既共享数据也共享行为,应用继承
  • 当你想由基类控制接口时,用继承;想自己控制接口时,用包含

7.高质量的子程序

7.1创建子程序的理由

降低复杂度、引入抽象、避免重复、易于子类化(覆盖)、隐藏顺序、提高可移植性、简化布尔判断。。。。

7.2在子程序上设计
子程序设计的目标:提高内聚性,让每个子程序只把一件事情做好,不再做任何其他事。
最强也是最好的内聚性:功能的内聚性(functional cohesion),即一个子程序只执行一项操作。

7.3好的子程序名字
  • 描述子程序所做的所有事情,描述其所有的输出结果及副作用。(子程序最好不要产生副作用,只做一件事)
  • 避免使用无意义、模糊或表述不清的动词,如handleXXX、performXXX、outputXXX、processXXX、dealWithXXX。(这些动词命名之所以不明确,多数是因为子程序要解决的问题的目的不明确)
  • 给函数命名时要对返回值有所描述
  • 给过程命名时使用语气强烈的动宾短语,如printXXX、checkXXX。但在面向对象语言中,对象本身充当了宾语,因此函数名中不需要体现宾语,如xxx.print
  • 准确使用对仗词,如add/remove、first/last。。。。

7.4子程序的长度:50~150行,不要超过200行
与其对子程序的长度进行限制,不如让下面这些因素——子程序的内聚性、嵌套层次、变量数量、决策点数量、注释数量以及其他一些跟踪复杂度相关的考虑事项——来决定子程序的长度。

7.5如何使用子程序参数
  • 按照输入-修改-输出的顺序排列参数
  • 使用const/final关键字修饰参数
  • 子程序中相似的参数排列顺序保持一致
  • 使用所有参数,如果不使用它,就把它从接口中删除
  • 不要把输入变量作为工作变量,应使用局部变量
  • 在接口中对参数的假定加以说明——使用断言
  • 明确子程序的接口要表达何种抽象,并给予合适的参数




你可能感兴趣的:(【读书笔记】代码大全)