《代码大全》读书笔记
继续讨论继承。
遵循Liskov替换原则。这个原则的意思是,除非派生类真的是一个更特殊的基类,否则就不应该从基类继承,也就是说“派生类必须通过基类的接口而被使用,且使用时无需了解两者之间的差异。换句话说,对于基类中定义的所有子程序,用在它的任何一个派生类中时的含义都应该时相同的。
如果程序遵循Liskov原则,那么继承就会降低程序的复杂度,否则只会让程序更复杂。
派生类可以继承成员的接口和/或实现。
有时候并不是在派生类出问题,而是在基类出了问题,比如说如果基类包含了一个并非所有类都有的特征。
继承的总结:
如果多个类共享数据而非行为,那么应该创建这些类可以包含的共用对象。
如果多个类的共享行为而非数据,应该让它们从共同的基类继承而来,并在基类里定义共用的子程序。
如果多个类既共享数据也共享行为,应该让它们从一个共同的基类继承而来,并在基类里定义共用的数据和子程序。
当想用基类控制接口时,使用继承;当想自己控制接口时,使用包含。
对于成员函数和数据成员,一般来说,应尽量减小类与类之间合作的范围。要尽量使:所实例化的对象的种类、在被实例化对象上直接调用的不同子程序的数量、调用由其他对象返回的对象的子程序的数量尽量的少。
关于构造函数:
如果可能的话,应该在所有的构造函数中初始化所有的数据成员,是一个防御式编程实践。
用私用构造函数来强制实现单件属性。单件属性指的是只能由唯一的一个对象实例,那么就可以将所有构造函数隐藏起来。在编译阶段就调用构造函数实现唯一的实例。
创建类的原因有:为现实世界中的对象建模、为抽象的对象建模、降低复杂度(隐藏信息)、隔离复杂度(类内的复杂度不会扩展到整个程序)、隐藏实现细节、限制变动的影响范围(将最容易变动的部分设计成最容易修改的)、隐藏全局数据/让参数传递更顺畅、建立中心控制点。
关于子程序:
子程序的作用首先在于降低代码复杂度,其函数名应该具有抽象含义,其次子程序避免了代码重复、易于修改,并且简单的子程序更容易被子类化,也就是更容易被覆盖,再者就是隐藏各种细节,如指针细节,bool判断细节。
子程序的很多创建理由是和类很像的。对于子程序的设计,我们希望它有比较高的内聚性。功能的内聚性,也就是说让一个子程序仅执行一项操作。有一些内聚性是不可取的:首先是过程的内聚性:是指一个子程序中的操作是按特定的顺序进行的并且这些顺序从代码逻辑上是没有必要的。其次是逻辑上的内聚性:是只通过传入的控制标志选择执行其中的一项操作,这些操作本身没有任何关联。
子程序的名字的原则是:描述子程序所做的所有事情/避免使用无意义或模糊或表述不清楚的动词、不要仅通过数字来形成不同的子程序名称、给函数命名时要对返回值有所描述、给过程起名时使用语气强烈的动词加宾语的形式、准确地使用对仗词。
对于子程序的参数:如果几个子程序都用了类似的一些参数,应该让这些参数的排列顺序保一致。函数有返回值时,要检查所有可能的返回路径,在函数开头用一个默认值来初始化返回值是个很好的做,不要返回指向局部数据的引用或指针。
建立子函数的输入输出的概念。输入量最好不要被改变,也就是在操作时不要直接对输入量直接进行改动,引进新变量workingVal,来区别于输入变量。在接口中对参数的假定加以说明:参数是仅用于输入的、要被修改的、还是仅用于输出;表示数量的参数的单位;所能接受的数值的范围;
防御式编程:
保护程序免遭非法输入数据的破坏:检查所有来源于外部的数据的值。
使用断言:assertion. 断言为真,则表明程序运行正常,断言为假,则意味着它已经在代码中发现了意料之外的错误。一个断言通常包含两个参数:一个描述假设为真时的情况的表达式,和一个断言为假时余姚显示的信息。
用错误处理代码来处理预期会发生的状况,用断言来处理绝不应该发生的状况。断言用来检查代码中bug。
错误处理技术:
返回中立值:处理错误数据的最佳做法就是继续执行操作并简单地返回一个没有危害的数值。换用下一个正确的数据:在处理数据流的时候,有时候只要返回下一个正确的数据,那么如果有一条记录损坏,就跳过继续读,比如说读取温度计的数据。返回与前次相同的数据。换用最接近的合法值。返回一个错误码:可以决定只让系统的某些部分处理错误。其他部分则不在局部处理,而是简单的报告说有错误发生,并新人调用链上游的某个子程序会处理错误。通知系统其余部分已经发生错误可以采用以下方法:设置一个状态变量的值、用状态值作为函数的返回值/用语言内建的异常机制抛出一个异常。调用错误处理子程序或对象:把错误处理集中在一个全局的错误处理子程序或者对象中。该方法对安全性有一个影响就是如果代码缓冲区发生了溢出,那么攻击者可能已经篡改了这一处理程序或对象的地址。关闭程序。