摘要
之前的章节我们了解到构建对软件开发的重要性,接下来这几章我们将讨论构建类和子程序的细节来来达到提升代码质量的目的。
ADT:描述类的数据和操作(子程序)
好处:能够隐藏实现细节和内部数据,降低耦合,增加可读性,方便维护(发现错误不用一个个地方修改,只用修改一处,就是这个方法)。
类的接口应该展现一致的抽象层次
根据类就大概能知道类的作用
没有良好抽象的类,各类型混杂的方法职能不专一,会让程序越来越难以理解
一定要理解类所实现的抽象是什么
一些类很相似,需要理解类的接口需要的抽象是哪一个。可能使用的时候我们暂时都可以用,但是一定要仔细考虑。
提供成对的服务
创建子程序的时候,需要考虑是否需要创建与之互补的另一个操作。
把不相关的信息转移到其他类中。
可能一个类中两个不同的子程序分别使用到两部分不相交的数据,那么干脆可以将二者拆分成两个类。
尽可能让接口可编程,而不是表达语义
接口尽量少一些需要人为的在逻辑上需要判断的操作(这类操作必须加注释)。而是尽量拿来就很容易使用,这样再复用或修改代码的时候不容易出问题。
谨防在修改时破坏接口的抽象
就是后续维护代码,修改代码时若是不严格遵守最初的抽象理念,随意添加不一致的子程序,这样会偏离接口的最初用途,代码变得越来越混乱。
不要添加与接口抽象不一致的公用成员
尽可能地限制类和成员的可访问性
避免公开成员数据,少用protected降低派生类和基类间的耦合,在不是很坚定的情况下,尽量选择更严格的访问级别。
避免把私用的实现细节放入类的接口中
–避免使用友元类
–要格外警惕从语义上破坏封装性
–不要对类的使用者做出任何假设
–不要因为一个子程序里仅仅只用了公用–子程序就将其归入公开接口,而应考虑暴露后展现的抽象是否一致
提高代码可读性比加快编写代码更重要
包含(“has a…” 的关系)
通过包含来实现"有一个/has a"的关系,如类数据成员
万不得已时通过private继承来实现,主要原因是让外层的包含类可访问内层被包含类的protected成员
警惕超过7个数据成员的类,7±2是人们在做其他事情时能记住的离散项目个数
成员函数和数据成员
让类中子程序数量尽可能减少(减少耦合)
减少类所调用的不同子程序的数量:研究表明类中错误数量和类所调用不同子程序的总数是相关的。
总的来说就是减小类和类之间相互合作的范围,尽可能让以下几个数字最小:
1、所实例化的对象的种类
2、被实例化对象上直接调用的不同子程序的数量
3、调用由其他对象返回的对象的子程序数量
避免创建万能类
消除无关紧要的类
避免用动词命名的类
什么是低质量的子程序?
如图我们能发现几个问题:
1、名字很差,handlestuff无法知道这个程序是干嘛的
2、没有文档说明
3、布局随意,风格很多,看着很混乱
4、入参被修改,输入变量值不应该被修改,否则不该叫做inputRec
5、子程序没有单一目的(既有初始化变量向数据库写数据,又有一些计算操作,彼此没有明显联系)
6、未防范数据错误,crntQtr若是0则程序会报错(这里java开发很常见,未校验空指针,就直接使用其内部方法)
7、魔法值
8、无效代码,有一些未被使用的变量。
9、入参太多,一般上限书中说是7个(实际开发,个人感觉很少超过5个)
10、入参顺序混乱,而且没有注释
内聚性:
指的是子程序中各种操作联系的紧密程度
研究表明,拥有大量子程序的代码,低内聚性的代码出错率是高内聚性的7倍
举例:两个方法,一个cos()一个cosAndTan().
这两个哪个内聚性强,哪个弱??
顺序上的内聚性(es聚合查询的代码,多项子程序共享数据,所有操作加起来才是一项完整功能)
通信上的内聚性
指的是多项不同的操作使用了同样的数据(我理解某个类中的全局变量如正则)
临时的内聚性
比如某些操作需要执行的时机相同,比如启动微服务时的一些操作
过程上的内聚性,(比如银行自助机上注册个人信息,根据提示一步一步,实际上后台代码中每一步并没有关联、数据也互不影响,但可以与操作过程对应,形成过程内聚)
逻辑上的内聚性
仅仅因为程序的控制流,所谓的逻辑操作(比如if、case)而放到一起的操作成为逻辑上的内聚
防御式编程的主要思想:
不是指编程时抱着防备别人批评和攻击的态度,而是建立一种永远不知道他人将如何使用你的程序的思想。
我们在编写函数时防范程序因输入错误的数据而遭到破坏。更通俗来说就是要承认程序都会有问题、都需要被修改。聪明的程序员都是带着这种思想编程。
返回中立值
换用下一个正确的值
返回与前次相同的数
换用最接近的合法值
把警告信息记录到日志文件中
返回错误码
错误发生时返回错误信息
关闭程序
健壮性和正确性
正确性意味着永远不返回不正确的结果;
健壮性意味着要不断尝试采取某些措施,以保证软件继续运行,哪怕程序出点错误。
既然有那么多种方式处理错误,我们就必须注意,应该在整个程序里采用一致的错误处理方法。
异常是把代码中的错误或异常事件传递给调用方代码的一种特殊手段。
让软件的某些部分处理“不干净的数据”,另一部分处理"干净"数据,即可让大部分代码无须再担负检查错误数据的职责。
进攻式编程就是主动尝试激发程序的错误,只有开发时经历惨痛的失败,才不会让你发布产品后败的太惨