收藏 分享(赏)

《代码大全》+学习笔记.pdf

上传人:精品资料 文档编号:9508920 上传时间:2019-08-11 格式:PDF 页数:62 大小:507.80KB
下载 相关 举报
《代码大全》+学习笔记.pdf_第1页
第1页 / 共62页
《代码大全》+学习笔记.pdf_第2页
第2页 / 共62页
《代码大全》+学习笔记.pdf_第3页
第3页 / 共62页
《代码大全》+学习笔记.pdf_第4页
第4页 / 共62页
《代码大全》+学习笔记.pdf_第5页
第5页 / 共62页
点击查看更多>>
资源描述

1、代码大全学习笔记常高伟2009-9-28目录目录目录目录第五章,软件构建中的设计.4启发式设计方法:.5找出容易改变的区域:(核心在于隐藏变化,与上面的隐藏信息对应)5保持松散耦合:.65.4设计实践85.5对流行的设计方法的评论. 9核对表:软件构造中的设计 10本章要点.10第六章,可以工作的类.11前言.116.1类的基础:抽象数据类型(ADTs)116.2良好的类接口126.3有关设计和实现的问题. 136.4创建类的原因166.5与具体编程语言相关的问题 176.6超越类:包17CHECKLIST: ClassQuality.18核对表:类的质量.18本章要点.19第七章,高质量的子

2、程序.19前言.197.1创建子程序的正当的理由. 207.2在子程序上的设计. 217.3好的子程序名字227.4子程序可以有多长. 237.5如何使用子程序的参数. 237.6使用函数时要特别考虑的问题. 247.7宏子程序和内联子程序. 24CHECKLIST: High-QualityRoutines26核对表:高质量的子程序 26本章要点.26第八章,防御式编程.27前言.278.1保护数据免受非法数据的破坏. 278.2断言288.3错误处理技术288.4异常298.5隔离程序,使之包容有错误造成的损害. 298.6辅助调试的代码308.7确定在代码中保存多少防御式代码. 30CH

3、ECKLIST: DefensiveProgram m ing . 32核对表:防御式编程.32本章要点.33第九章,伪代码编程过程.33前言.339.1创建类和子程序的步骤概述. 339.2伪代码349.2通过伪代码编程过程创建子程序. 34CHECKLIST:ThePseudocodeProgram m ing Process36核对表:伪代码编程过程 36本章要点.36第十章,使用变量的一般事项 37前言.3710.1数据认知3710.2轻松掌握变量定义. 3710.3变量初始化原则3710.4作用域3810.5持续性3910.6绑定时间4010.7数据类型和控制结构之间的关系. 401

4、0.8为变量指定单一用途. 40CHECKLIST: General ConsiderationsInUsingData. 41核对表:使用数据的一般注意事项 41本章要点.41第十一章,变量名的力量.42前言.4211.1选择好的变量名的注意事项. 4211.2为特定类型的数据命名4311.3命名规则的力量4411.4非正式命名规则4411.7应该避免的名字44第十二章,基本数据类型.45前言.4512.1数值概论4512.2整数4512.3浮点数4512.3字符和字符串4612.6枚举类型46第十三章,不常见的数据类型 46前言.4613.1结构体4613.2使用指针的一般技巧. 4613

5、.3全局变量47第十四章,组织直线型代码 48前言.4814.1必须有明确顺序的语句. 4814.1顺序无关的语句48CHECKLIST: OrganizingStraight-lineCode 49核对表:组织直线型代码 49本章要点.49第十五章,使用条件语句.49前言.4915.1if语句. 4915.1case语句.50第十六章,控制循环.51前言.5116.1选择循环的种类5116.2循环控制5116.3轻松创建循环由内而外. 53CHECKLIST: Loops.54核对表:循环.54本章要点.55第十七章,不常见的控制结构 55前言.5517.1子程序中有多处返回. 5517.2

6、递归5517.2goto56CHECKLIST: Unusual Control Structures.56核对表:不常见的控制结构 56本章要点.56第十八章,表驱动法.57前言.5718.1表驱动法使用总则. 5718.2直接访问表5718.3索引访问表5818.4阶梯访问表5818.5表查询的其他示例. 58CHECKLIST:Table-DrivenMethods 58核对表:表驱动法.58本章要点.58第十九章,一般控制问题.59前言.5919.1布尔表达式5919.2复合语句6019.3空语句6019.4驯服危险的深层嵌套. 6019.5编程基础:结构化编程. 6119.6控制结构

7、与复杂度. 61CHECKLIST: Control-Structurelssues62核对表:控制结构相关事宜 62本章要点.62第二十章,软件质量概述.63前言.6320.1软件质量的特性6320.2改善软件质量的技术. 64第二十一章,协同构建.64前言.6421.164第二十二章,开发者测试.64前言.6422.1开发者测试在软件开发中的角色. 64第二十五章,代码调整.65前言.65第五章,软件构建中的设计第五章,软件构建中的设计第五章,软件构建中的设计第五章,软件构建中的设计1、“深入一种语言去编程”,而非“在一种语言上去编程”。区别在于,不要将自己的思想受限于语言特性,而是首先决

8、定表达的思想,然后使用特定的语言特性来表达这些实现。2、软件设计的首要问题:管理复杂度。管理复杂度的两个行之有效的方法:1)把任何人同一时间需要处理的本质复杂度的量减少到最少所有软件设计的目标都是把复杂问题分解成简单的部分。子系统间的相互依赖越少,你就越容易在同一时间只专注于问题的一小部分。精心设计的对象关系是关注点相互分离,从而使你在每一时刻只专注于一件事情。在软件架构层次上,可以将整个系统分解为多个子系统来降低问题的复杂度,为了减少子系统间的相互依赖,子系统尽量设计成松散耦合。要避免做成聪明的设计,因为聪明的设计常常是难于理解的。应该做出简单且易于理解的设计。如果你的程序设计方案不能让你在

9、专注于程序的一部分时安心的忽视其他部分的话,这一设计就没有什么作用了。2)不要让偶然复杂度无谓的快速增长偶然的复杂性包括后期需求更改,需求不明确等。可以通过软件过程加以控制。3、编程首先是要让人理解的,其次才是计算机。4、关于子系统的设计,从管理复杂度的角度触发,要对子系统间的通信加以限制,否则子系统间的耦合会是划分子系统的好处消失。系统层的设计应该是无环图?启发式设计方法:1、抽象:是一种能让你在关注某一概念的同时可以放心的忽略其中的一些细节的能力,在不同的层次上处理不同的细节。2、封装:抽象是“从更高层次的细节来看待一个对象”,封装是“除此之外,你不能看到对象的任何其他层次的细节”。封装帮

10、助你管理复杂度的方法是不让你看到那些复杂度。3、信息隐藏:信息隐藏的好处是,对减少“改动影响的代码量”至关重要。需要隐藏的类型一般包括:隐藏复杂度:这样你就不用去对付它,除非需要的时候。隐藏变化源:这样变化发生时,其影响就被限制在局部的范围内。4、信息隐藏的障碍:信息过度分散:通过子程序来统一管理过度分散的信息。循环依赖:A类调用B类,B类又调用A类。我的代码中有很大这样的用法,看有什么方法可以解决?在设计的时候,要多问“我该隐藏些什么?”。找出容易改变的区域:(核心在于隐藏变化,与上面的隐藏信息对应)1、尽量使用枚举来替换bool常量。2、识别容易变化的区域:业务规则:硬件依赖:非标准语言特

11、性:底层库:困难的设计区域和构建区域:状态变量:数据量的限制:3、预料不同程度的变化:应该让变化的影响或范围与发生该变化的可能性成反比,即:如果变化容易发生而又容易做出计划,你就应该在它上面多下功夫,不要浪费过度的精力在不太可能发生而又很难做出计划的变化上。4、找出容易变化的区域的一个好的方法:首先找到程序中可能对用户有用的最小子集,这一最小子集构成了系统的核心,不容易发生变化。接下来,有微小的步伐扩充这个系统。当你考虑功能上的改进时,同时也考虑质的变化。这些潜在的改进区域就构成了系统中的潜在的变化。保持松散耦合:1、耦合度表示类和类之间或者子程序和子程序之间关系的紧密程度,耦合度设计的目标是

12、创建出小的,直接的,清晰的类或子程序,使他们与其他类或子程序之间的关系尽可能的灵活,这就被称作“松散耦合”。2、模块间好的耦合关系会松散到恰好能使一个模块能够很容易地被其他模块使用。3、请尽量使你创建的模块不依赖或者很少依赖其他模块。4、耦合标准:规模:指模块间的连接数。规模越小,耦合度越松散。可见性:模块间连接的显著程度。连接要尽可能的明显。灵活性:模块间的连接是否容易改动。总结:一个模块越容易被其他模块调用,那么他们之间的耦合关系就会越松散。创建系统架构时,按照“尽可能的缩减相互连接”的准则来分解程序。5、耦合的种类:简单数据参数耦合:可以接受。简单对象耦合:一个模块实例化一个对象。对象参

13、数耦合:对象作为参数传递数据,比数据参数耦合更紧密。语义耦合:一个模块不仅使用了另一个模块的用法元素,而且还使用了有关那个模块内部工作细节的语义知识。6、松散耦合的关键之处在于,一个有效的模块提供出了一层附加的抽象(?是不是这个抽象屏蔽了内部的细节,而是对此模块的调用更容易)。一旦你写好了它,你就可以想当然的去使用它,这样就降低了整体系统的复杂度。使你在同一时间只关注一件事情。如果一个模块的使用要求你同时关注几件事情内部工作的细节,对全局数据的修改,不确定的功能点那么就失去了抽象的能力,模块所具有的管理复杂度的能力也削弱或完全丧失了。类和子程序是降低复杂度的首选和最重要的智力工具,如果他们没有

14、帮助你简化工作,那就是他们的失职。设计模式:1、设计模式通过提供现成的抽象来减少复杂度。2、设计模式通过把常见解决方案的细节予以制度化来减少出错。(设计模式我还要好好看看)3、设计模式通过多种设计方案而带来启发性的价值。4、设计模式通过把设计对话提升到一个更高的层次上来简化交流。5、陷阱:强迫让代码适应某个模式,或者为了模式而模式。这两者都不可取,好的设计应该是巧好能够解决现实问题的设计。其他的启示方法:1、高内聚性:类内部的子程序或子程序内部的代码在支持一个中心目标的紧密程度。2、构造分层结构:通过分层来管理复杂度,同一时间,只关注本层的细节,而不是在所有的时间都要考虑所有的细节。3、严格描

15、述类契约:?4、分配职责:考虑:该怎样为对象分配职责,对象应该隐藏什么信息。5、为测试而设计:考虑,怎样设计系统会更容易测试?类似于测试驱动开发。6、避免失误:在设计时,考虑失败的案例,有可能有反面的启发意义。7、画一个图8、保持设计的模块化:使用启发式设计方法的原则:1、不要卡在一个方法上。2、你无须马上解决整个难题。5.4设计实践1、迭代:从不同的视角来考虑问题。2、分而治之:增量式改进。3、自上而下和自下而上:区别在于,前者是一种分解策略,后者是一种合成策略。4、建立设计原型。5、合作设计。设计做多少才够1、两个问题:一个设计的层次(详细程度),另一个是记录设计的文档的正规程度。2、受影

16、响的因素包括:项目规模(正比),项目生存周期(正比),项目成员经验(反比)3、最大的失误来自于我认为自己已经做得很充分,可事后却发现还是做得不够,没能发现其他的一些设计挑战。4、最大的设计问题通常不是我认为是很困难的,并且在其中做了不好的设计的区域;而是我认为是简单的,而没有做出任何设计的区域。5、程序化的活动容易把非程序化的活动驱逐出去Gresham法则6、宁愿花费80%的精力去创建和探索大量的备选方案,而用20%的时间去创建并不是很精美的文档,也不愿意把20%的精力花在创建平庸的设计方案上,而把80%的精力用于对不良的设计进行抛光润色。记录设计成果1、把设计文档插入到代码中;2、用wiki

17、来记录设计讨论和决策;3、写总结邮件;4、使用数码相机保存设计结果。这种方法可以考虑一下。5、UML图。学习一下。5.5对流行的设计方法的评论1、设计一切和不做设计都是不可取的,比较合理的做法是,预先做一点设计,或者预先做足够的设计。2、如果你无法判断最佳的设计量,请记住:设计所有细节和不做设计一定是不对的。P.J. Plauger:“你在应用某种设计方法的时候越教条化,你所能解决的现实问题就会越少。请把设计看出是一个险恶的,杂乱的和启发式的过程(没有教条化的规则可以遵循,能够巧好解决现实问题的方案才是最佳的方案)。不要停留于你所想到的第一套方案,而是去寻求合作,探求简洁性,在需要的时候做出原

18、型,迭代,并进一步迭代。你将对你的设计成果感到满意。”由此,对概要设计文档,形式上没有必要要求太高,设计的层次上,应该是更高层面上的概要设计。开发流程当中,概要设计阶段对设计结果进行评审还有一个好处,就是在设计者向评审人员讲解设计方案时,有助于他对自身的设计方案重新审视而获得不一样的设计思路。本章要点软件的首要技术使命就是管理复杂度。以简单性作为努力目标的设计方案对此最有帮助。核对表:软件构造中的设计设计实践你已经做过多次迭代,并且从众多尝试的结果中选择最佳的一种,而不是简单的选择第一次尝试的结果吗?你尝试用多种方案分解系统,以确定最佳方案吗?你同时用之上而下和自下而上的方法来解决设计问题吗?

19、为了解决某些特定的问题,你对系统中的风险部分或不熟悉的部分创建过原型、写出数量最少的可抛弃的代码吗?你的设计方案被其他人检查了吗?你一直在展开设计,直到设计细节跃然纸上了吗?你用某种适当的技术比如说wiki、电子邮件、挂图、数码照片、UML、CRC卡或代码注释来保留你的设计成果吗?设计目标你的设计是否充分的处理了有系统架构层定义出并且推迟确定的事项?你的设计被划分为层次吗?你对把这一程序分解层子程序、包和类的方式感到满意吗?你对把类分解成为子程序的方法感到满意吗?类和类之间的交互关系是否已被设计为最小的了?类和子程序是否被设计为能够在其他的系统中重用?程序是否易于维护?设计是否精简?设计出来的

20、每一部分都绝对必要吗?设计中是否采用了标准的技术?是否避免使用怪异且难以理解的元素?整体而言,你的设计是否有助于最小化偶然的和本质的复杂度吗?简单性可以通过两种方式来获取:一是减少在同一时间所关注的本质性复杂度的量,二是避免生成不必要的偶然的复杂度。设计是一种启发式的过程。固执于某一种单一方法会损害创新能力,从而损害你的程序。好的设计都是迭代的。你尝试设计的可能性越多,你的最终设计方案就是变得越好。信息隐藏是个非常有价值的概念。通过询问“我应该隐藏什么,能够解决很大困难的设计问题”。第六章,可以工作的类第六章,可以工作的类第六章,可以工作的类第六章,可以工作的类前言1、成为高效程序员的一个关键

21、就在于:当你开发程序任何一部分代码时,都能安全的忽视程序中尽可能多的其余部分。6.1类的基础:抽象数据类型(ADTs)抽象数据类型定义ADT(abstract datatype),是指一些数据以及对这些数据所进行的操作的集合。使用ADT的益处:1、可以隐藏实现细节。2、改动不会影响到整个程序。3、让接口提供更多的信息。4、更容易提高性能。5、让程序的正确性更显而易见。6、让程序更具有自我说明性。7、无须在程序内部到处传递数据。8、你可以像在显示世界中那样操作实体,而不用在底层实现细节上操作他。6.2良好的类接口创建高质量类的第一步,可能也是最重要的一步:创建一个好的接口。好的抽象定义类的时候,

22、把public放在最前面,使用类的时候,一下就可以看到公共的方法。1、类的接口应展现一致的抽象层次。每一个类应该实现一个ADT,并且仅实现这个ADT。如果你发现一个类不止实现了一个ADT,或者不能确定究竟它实现了何种ADT,你就应该把这个类重新组织为一个或多个定义更加明确的ADT。所谓层次一致,就是要所有的接口都是围绕ADT中的“data”来展开的。并且,要对实现的细节进行隐藏。2、一定要理解类实现的抽象是什么。3、提供成对的服务。大多数接口都有相应的,相等的,相反的操作。4、把不相关的信息转移到其他类中5、尽量让接口可编程,而不是表达语义。编程部分有接口中的数据类型和其他属性构成,编译器强制

23、要求他们。语义部分则由“本接口将会怎样被使用”的假定组成,编译器无法强制检查,比如,接口A应该在接口B调用前调用,否则会引起崩溃;调用接口A前需要设置全局变量。等。断言可以将语义部分转换为编程部分?深入研究一下断言。6、谨防在修改时破坏接口的抽象。流程编辑中的Node类现在有点杂乱了。7、不要添加与接口抽象不一致的公用成员,每次向类中的接口中添加子程序时,问问“这个子程序与现有接口提供的抽象一致吗?”,如果不一致,该如何解决?我遇到过类似的问题,可以参考一下设计模式8、同时考虑内聚性和抽象性。“关注类的接口所表现出来的抽象,比关注类的内聚性更有助于深入的理解类的设计。”良好的封装“设计精良的模

24、块和设计糟糕的模块的唯一最大区别,就是对其他模块隐藏本模块内部数据和其他实现细节的程度。”JoshuaBloch。封装是比抽象更强的一个概念。抽象通过让你忽略实现细节的模型来管理复杂度,而封装则强制阻止你看到细节。1、尽可能限制类和成员的可访问性。让可访问性尽可能的低是促成封装的原则之一。如果无法确定子程序的访问级别(公用,私用,受保护),经验之举是采用最严格且可行的访问级别。更好的建议:采用那种访问级别能够最好的保护接口抽象的完整性?2、不要公开暴露成员数据。3、避免把私用的实现细节放入类的接口中。不要在private中暴露内部细节(我现在基本都是这样的),一个做法就是可以在private中

25、定义一个指针,指向一个附属类中,附属类中实现了有关的细节。4、不要对类的使用做出任何的假设。即尽量让接口可编程,而不是表达语义。5、避免使用友元类。State模式中按照正确的方式使用友元类有助于管理复杂度。但一般友元会破坏封装(为什么。查一下。)。他让你在同一时刻考虑更多的代码。6、不要因为一个子程序仅使用公用子程序,就把它归入公开接口。7、让阅读代码比编写代码更方便。8、要格外警惕从语义上破坏封装性。语义上对封装性的破坏很大:它们让调用代码不是依赖于类的公开接口,而是依赖于类的私用实现。每当你发现自己是通过查看类的内部实现来得知该如何使用这个类的时候,你就不是针对接口编程了,而是透过接口针对

26、内部实现编程了。如果你透过接口来编程的话,封装性就被破坏了,而一旦封装性开始遭到破坏,抽象能力也就遭殃了。我的代码中很多地方都是这样使用的。后面要好好的看看。目前我还找不到办法解决这个问题,有可能是设计的问题。要从根本上解决。以前对针对接口编程不是很理解。现在终于理解了。9、留意过于紧密的耦合关系。建议:1)在基类中把数据声明为private,而不是protect,以降低派生类和基类的耦合关系。避免在公开接口中暴露成员数据。要对从语义上破坏封装性保持警惕。紧密的耦合性总是发生在抽象不严谨或封装遭到破坏的时候。6.3有关设计和实现的问题这一节主要是关于类内部的设计和实现。包含(“有一个”的关系)

27、包含表示一个类含有一个基本数据元素或对象,继承比包含复杂的多,不是因为继承比包含更好。包含才是面向对象中的主力技术。1、包含表示有一个的关系。2、在万不得已是通过private继承来实现有一个的关系。3、警惕有超过约7个数据成员的类。如果超过7(+-)2个成员,考虑把这个类分解成更小的类。简单数据成员上限9个,复杂对象5个。主要的原理就是分而治之,降低单个复杂度。继承(“是一个”的关系)使用继承是考虑:1、对以每一个成员函数,它应该对派生类可见吗?它应该有默认的实现吗?这一默认实现能被覆盖吗?2、对每个成员数据:是否对派生类可见?更详细的说明:1、用public继承来实现“是一个”的关系。能否

28、用private来实现继承,会有什么效果?三种不同的继承:1)public类型继承。表示是一个的关系。2)private实现继承,基类中所有的公共接口和公共成员都是派生类中的私有接口。3)protect继承,所有的公用成员和方法都将成为派生类的protcet成员。2、要么使用继承并进行详细的说明,要么就不使用它。使用继承程序增加复杂度,是一种危险的技术,要尽量少用。就像我的serviceinterface一样,现在有点过于复杂。3、遵循替换原则:派生类必须能够通过基类的接口而被使用,且使用者无须了解两者之间的差异。如果程序遵循替换原则,继承就能够成为降低复杂度的一个强大工具,因为它让程序员关注

29、于对象的一般特性而不必担心细节。4、确保只继承需要继承的部分:1)抽象且可覆盖继承接口,不继承实现(纯虚接口)。2)可覆盖继承接口,默认实现,并且可以覆盖默认实现(虚拟接口)。3)不可覆盖继承接口,默认实现,但是不可覆盖(一般public接口)。继承接口和继承实现都可以继承。如果只是使用实现,则最好使用包含(类的组合)形式。5、不要覆盖一个不可覆盖的成员函数派生类中的成员函数和基类中不可覆盖函数不要重名。6、把公用的接口,数据,操作放到继承树中尽可能高的位置,这样方便派生类的使用。高的标尺:根据抽象性来决定,如果把一个子程序移到更高的层次后会破坏该层对象的抽象性,就要停止了。7、只有一个实例的

30、类是值得怀疑的。有可能把对象和类混为一谈了。考虑能否创建新的对象(派生类中的差异通过数据而非定义新的类来表达)。单件模式例外。8、只有一个派生类的基类也值得怀疑。我的GNodeData设计不是很合理。考虑是否可以把它在优化一下。这样是在做“提前设计”试图去预测未来的需要,而又常常没有真正了解为了到底需要什么。为未来变化要做的不是创建几层额外的基类,而是让眼下的工作成功尽可能的清晰,简单,直截了当。不要创建任何并非绝对必要的几层结构。9、派生后覆盖了某子程序,当中其中没有进行任何操作,这种情况也值得怀疑。这种情况通常是基类中的设计有问题。破坏接口语义,破坏接口抽象,增加维护难度。10、避免继承体

31、系过深。过深的继承体系会导致复杂度的增加,与首要技术使命背道而驰。11、尽量使用多态,避免大量的类型检查。要根据不同的情况的。12、让所有的数据是private,而非protected。继承会破坏封装。增加基类和派生类间的复杂度。继承往往会让你和程序员的首要技术使命(管理复杂度)背道而驰。从控制复杂度的角度说,你应该对继承持有非常歧视的态度。总结:何时可以使用继承,何时使用包含:1、如果多个类共享数据而非行为,应该创建这些类可以包含的共用对象。2、如果多个类共享行为而非数据,应该使用继承。3、如果多个类即共享数据,又共享行为,应该让它们从一个共同的基类继承而来,并在基类中定义共用的数据和子程序

32、。4、当你先通过基类控制接口时,使用继承。当你想自己控制接口是,使用包含。成员函数和数据成员1、让类中子程序的数量尽可能的少。需要和其他竞争的因素权衡一下:过深的继承体系,类中大量调用子程序。2、禁止隐式的产生你不需要的成员函数和运算符。3、减少类所调用的不同子程序的数量。4、对其他类的间接调用尽可能的少。构造函数1、如果可能,应该在所有的构造函数中初始化所有的数据成员。2、用私用(private)构造函数来强制实现单件属性。3、优先采用深层复本(?)(deepcopies),除非论证可行,才采用浅层复本。(可能是对象拷贝的概念)。6.4创建类的原因创建类的原因1、为现实世界中的对象建模。2、

33、为抽象的对象建模。抽象对象能够对具体的对象提供抽象。“从现实世界中提炼出抽象的概念”。设计出恰当的抽象对象是面向对象设计中的一项主要挑战。3、降低复杂度。这是创建类的最重要的理由。通过抽象,封装,信息隐藏降低复杂度。4、隔离复杂度。将复杂的东西,隔离在一定的范围内。5、隐藏实现细节。6、限制变动的影响范围。7、隐藏全局数据。把全局数据放到类的接口后面,通过访问器子程序(accessroutine)来操控全局数据有很多好处:可以修改数据结构而无须修改程序本身;监视对数据的访问;会使你思考数据是否是全局的;你会发现,全局数据只是对象数据而已。8、让参数传递更顺畅。用类中多个子程序共享数据,代替数据

34、的大量传递。9、建立中心控制点。集中控制有助于隐藏细节。10、让代码更易于重用。精心分解的一组类,比一个功能很强大的大类更利于重用。11、为程序族做计划。其实是一种预先设计的方法。根据实际情况,把需要更改的东西放在一个类中。后面出现更改需求的时候,直接替换这个类。12、把相关操作包装到一起,13、为某种特定的重构。应该避免的类:1、避免创建万能类。2、消除无关紧要的类。3、避免用动词命名类。只有行为,没有数据的类不是一个真正类。6.5与具体编程语言相关的问题6.6超越类:包如果我们有更好的工具把对象聚合起来,我们就可能更好的朝着抽象和封装的目标前进了。可以创建自己的包,如果编程语言不支持包。1

35、、用于区分公共的类和私用的类的命名规则。2、为了区分每个类所属的把而制定的命名规则和/或代码组织规则。3、规定什么包可以用其他什么包的规则,包括是否可以使用继承和包含等。这个变通之法展示的是“在一种语言上编程”和“深入一种语言去编程”之间的区别的好例子。CH ECK LIST:H KH KH K ClassQ ualityQ Q Q核对表:类的质量抽象数据类型1、你是否把程序中的类都看做抽象数据类型了?是否从这个角度评估他们的接口了?抽象1、类是否有一个中心目的?2、类的命名是否恰当?是否表达了其中心目的?3、类的接口是否展示了一致的抽象?4、类的接口是否能让人清楚明白的知道该如何用它?5、类

36、的接口是否足够抽象,使你能不必顾虑它是如何实现其服务的?你能把类看成黑盒子吗?6、类提供的接口是否足够完整,能让其他类无须动用其内部数据?7、是否从类中去除无关信息?8、是否考虑把类进一步分解为组件类?是否已尽可能将其分解?9、在修改类时是否维护了其接口的完整性?封装1、是否把类的成员的可访问性降到最小?2、是否避免暴露类中的数据成员?3、在编程语言所许可的范围内,类是否尽可能地对其他的类隐藏了自己的实现细节?4、类是否避免对其使用者,包括其派生类会如何使用它做了假设?5、类是否不依赖于其他的类?它是松散耦合的吗?继承1、继承是否只用来建立“是一个/isa”的关系?也就是说,派生类是否遵循了L

37、SP(Liskov替换原则)?2、类的文档中是否记述了其继承策略?3、派生类是否避免了“覆盖”不可覆盖的方法?是否把公用的接口、数据和行为都放到尽可能高的继承层次中了?4、继承层次是否很浅?5、基类中所有的数据成员是否都被定义为private,而非protected?跟实现相关的其他问题1、类中是否只有七个或者更好的数据成员?2、是否把类直接或间接的调用其他类的子程序的数量减少到了最少?3、类是否在绝对必要时才与其他的类相互协作?4、是否在构造函数中初始化了所有的数据成员?5、除非拥有经过测量的、创建浅层复本的理由,类是否都被设计为当作深层复本使用?6、是否尝试在更高的层次上(包)进行封装和抽

38、象?与语言相关的特性1、不是是否研究过所用编程语言里和类相关的各种特有问题?本章要点1、类的接口应该提供一致的抽象。很多问题都是由于违背了该原则而引起的。2、类的接口应该隐藏一些信息如某个系统接口、某项设计决策、或一些实现细节。3、包含往往比继承更为可取除非你要对“是一个”的关系建模。4、继承是一种有用的工具,但它会增加复杂度,这有违于软件的首要技术使命管理复杂度。5、类是管理复杂度的首先工具,要在设计类时给予足够的关注,才能实现这一目标。第七章,高质量的子程序第七章,高质量的子程序第七章,高质量的子程序第七章,高质量的子程序前言子程序是为实现一个特定的目的而编写的一个可被调用的方法或过程。7

39、.1创建子程序的正当的理由1、降低复杂度。当内部循环或内部嵌套层次很深的时候,就要考虑从子程序中提取出新的子程序了。子程序以其抽象,封装,信息、细节的隐藏来实现管理复杂度的功能。2、引入中间、易懂的抽象。通过命名将一段代码重组织,使其更易理解。3、避免代码重复。4、支持子类化。便于子类覆盖。5、隐藏顺序。6、隐藏指针操作。指针操作可读性通常很差,且容易出错。7、提高可移植性。8、简化复杂的布尔判断。9、改善性能。隐藏实现的细节,有助于后面用更好的算法进行重新实现。10、确保子程序都很小总之,创建高质量的子程序,有助于使用抽象、封装,信息细节隐藏的手段,来降低复杂度。除此之外的理由:1、隔离复杂

40、度。2、隐藏实现细节。3、限制变化带来的影响。4、隐藏全局数据:是对全局数据的范围都通过子程序来实现。避免直接对全局数据的范围。这个要注意一下的,是个非常好的建议。5、形成中央控制点。6、促进可重用的代码。7、达到特定的重构的目的。编写有效子程序时,一个最大的心里障碍是不情愿为一个简单的目的而编写一个简单的子程序。事实证明,一个很好而有小巧的子程序会非常有用:既可以提高可读性,又便于扩展。7.2在子程序上的设计在设计类的时候,抽象和封装已经很大程度上取代了内聚性。但是在子程序这一层次上,内聚性仍是常用的启发式方法。对与子程序而言,内聚性是指子程序中各个操作之间的联系的紧密程度。我们的目标是让子

41、程序只把一件事情做好,不再做任何其他事情。内聚性是针对操作的概念。即操作具有内聚性。通常,一个操作指一个语句,或一个函数调用。一个是简单的操作,一个是复杂的操作。内聚性的一些概念,理解概念有助于思考如何让子程序尽可能的内聚。1、功能的内聚性:让一个子程序仅执行一项操作。这是最强的也是最好的内聚性。其他的内聚性则不够理想。2、顺序上的内聚性:子程序内包含按照特定顺序执行的操作,这些步骤需要共享数据,而只有全部执行完毕后,才完成了一下完整的功能。顺序上的内聚性设计成功能上的内聚性需要对顺序进行分解,形成多个功能更加单一的子程序。3、通信上的内聚性:子程序中的不同操作使用了同样的数据,当不存在其他任

42、何联系。优化的方法是将子程序分解为多个子程序。4、临时的内聚性:子程序中含有一些因为需要同时执行才放到一起的操作。优化方法是把临时性的子程序看成是一系列事件的组织者,即让它直接调用其他的子程序,而不是直接执行所有的操作。另外,可以通过函数的命名,表达子程序完成的功能。避免使用多个操作叠加的方法命名,因为它暗示子程序只有巧合的内聚性。除功能的内聚性外,其他类型的内聚性都是不可取的。要想办法避免。1、过程上的内聚性:子程序中的操作是按特定的顺序进行的,除此外没有任何联系。对子程序分解,把不同的功能纳入不同的子程序中,让调用方的子程序具有单一而完整的功能。2、逻辑上的内聚性:若干操作被放入同一个子程

43、序中,通过传入的控制标志选择执行其中的一项操作。各个操作间没有任何的逻辑关系,这些操作只是被包含在一个很大的case或if语句中。如果此子程序只是分发命令,根据命令调用不同的子程序,则这种做法也是不错的。其他的不好的情况,可以通过分解来进行优化。分解为多个独立的子程序。3、巧合的内聚性:子程序内部的各个操作间没有任何的关联。内聚性考虑的是在一个子程序内部,操作的关联程度。编写功能上的内聚性的子程序几乎总是可能的,因此把注意力集中于功能上的内聚性,从而得到最大的收获。使用上面的建议对我的流程引擎程序进行内聚性的优化,发现确实很有价值。值得在后面的设计和编码中坚持这种优化。如果一个子程序中,局部变

44、量的个数超过7个,则有可能是此子程序设计出现问题,有可能要优化一下。7.3好的子程序名字1、好的子程序的名字能够清晰的描述子程序所做的一切。2、描述子程序所做的所有事情子程序的名字应该能够描述其所有的输出结果及副作用。3、避免使用无意义的,模糊或表述不清的动词handle,perform,output,process,dealwith等。很多情况下,使用模糊不清的动词作为子程序的名称的根本原因是程序本身就不是完成一个清晰的功能,这个时候,就要考虑重新组织该程序,并且选择准确的词语描述子程序。4、不要仅通过数字来形成不同的子程序名称。5、根据需要确定子程序名称的长度。研究表名,变量名的最佳长度是

45、9到15个字符(过短则无法表达准确其含义,过长则有碍阅读)。子程序命名的重点是含义清晰。长短要视名字是否清晰而定。6、给函数命名是要对返回值有所描述。最好能精确描述返回的结果。7、给过程起名时使用语气强烈的动词加宾语的形式。如果是面向对象,且动词是对对象的操作,则不用加宾语。8、准确使用仗词(成对的词语):close/open。9、为常用操作确立命名规则。7.4子程序可以有多长子程序的长度尽量控制在200行以内。在这个范围内,代码长度不是子程序的关键因素,其他的降低复杂度的因素才是关键的。7.5如何使用子程序的参数子程序间的接口是程序中最容易出错的部分之一。1、按照输入-输出-修改的顺序排列参

46、数:仅作输入参数-即做输入又作输出-仅作输出。2、考虑创建自己的OUT和IN关键字:有弊端,C+中可以使用const来表达只是输入的含义。3、如果几个程序都用了类似的一些参数,应该让这些参数的排列顺序保持一致。4、使用所有的参数。如果没有使用,则从接口中删除。5、把状态或出错变量放在最后。6、不要把子程序的参数用作工作变量。7、在接口中对参数的假定加以说明。比注释更好的做法是加断言:1)参数是仅用于输入,输出还是修改;2)表示数量的参数的单位;3)如果没有用枚举,则要说明状态代码和错误值的含义。4)所能接收的范围。5)不应该出现的特定数值。8、把子程序的参数限定在大约7个以内。7是一个神奇的数

47、值,人很难记住超过7个单位的信息。如果子程序的参数过多,则要考虑使用设计的方法进行优化。9、可以考虑采用某种表示输入,输出,修改的命名规则。10、为子程序传递用以维持其接口抽象的变量或对象。考虑这个问题的时候,角度应该是这个子程序的接口要表达何种抽象。如果传递对象能够表达子程序的抽象,就传递对象,如果传递变量能够表达抽象,就传递变量。如果传递对象,但是在调用子程序前后经常需要进行装配和卸载,或者传递变量,但是经常要修改参数表,则要考虑是否要换一种方式来设计接口。11、使用具名参数?12、确保形式参数和实际参数匹配。C语言存在这个问题,c+,java不存在。因为他们是强类型的语言。7.6使用函数

48、时要特别考虑的问题在现代编程语言中,函数是指有返回值的子程序,过程是指没有返回值的子程序。1、什么时候使用函数,什么时候使用过程:把子程序的调用和对状态值的判断清楚的分开,降低此语句的复杂度。如果一个子程序的主要用途就是返回其名字所指明的返回值,那么就使用函数,否则,使用过程。2、设置函数的返回值:1)检查所有的路径,设置一个默认的函数返回值。2)不要返回指向局部的应用和指针。7.7宏子程序和内联子程序1、把宏表达式包含在整个括号内:#definecube(a) (a)*(a)*(a)2、把多条语句的宏用括号括起来。3、用子程序的命名方法来命名宏,便于后面替换。4、不到万不得已,不要用宏来替换

49、子程序。5、限制使用内联子程序。Inline关键字可以支持提高性能。CH ECK LIST:H KH KH K H igh-Q ualityH QH QH Q Routines核对表:高质量的子程序大局事项1、创建子程序的理由充分吗?2、一个子程序中所用适用于单独提出的部分是不是已经被提出到单独的子程序中了?3、过程的名字中是否用了强烈、清晰的“动词+宾语”词组?函数的名字是否描述了其返回值?4、子程序的名字是否描述了其所做的全部的事情?5、是否给常用的操作建立了命名规则?6、子程序是否具有强烈的功能上的内聚性?即它是否做且制作一件事情,并且把它做得很好?7、子程序间是否有较松散的耦合?子程序和其他子程序之间的连接是否是小的(sm all)、明确的(intim ate)、可见的(viaible)和灵活的(flexible)?8、子程序的长度是否是由其功能和逻辑自然决定,而非遵循任何人为的编码标准?参数传递事宜1、整体来看,接口的参数是否表现出一种具有整体性且一致的接口抽象?2、子程序的参数排列顺序是否合理?是否与类似的子程序的参数排列顺序一致?3、接口假定是否已经在文档中说明?4、子程序的接口是否没有超过7个?5、是否用到了每一个输入参数?6、是否用到了每一个输出参数?7、子程序是否避免了把输入参数用做工作变量

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报