这段时间代码质量很差,重新捡起有段时间没看的《代码大全》。以前培训的时候是很多人一起看,这次准备看一些对于工作帮助比较明显或者说工作中急需提高的章节。
下面主要摘录书中的一些比较经典或者说比较重要的内容,在有些地方也加上我自己的一些感受。
成为高效程序员的一个关键就在于,当你开发程序任何一部分代码时,都能安全地忽视程序中尽可能多的其余部分。
类的基础:抽象数据类型(ADTs)
1.抽象数据类型(ADT,abstract data type),是指一些数据以及对这些数据所进行的操作的集合。
使用ADT的益处
(1)可以隐藏实现细节:例如你可以把字体数据的类型信息隐藏起来,意味着如果数据的类型发生改变,只需要在一个地方修改而不会影响到整个程序。
(2)改动不会影响到整个程序
(3)让接口能提供更多信息
(4)更容易提高性能
(5)无需在程序内部到处传递数据
(6)可以像在现实世界中那样操作实体,而不用在底层实现上操作它。
2.良好的类接口
这里的接口指的是类中能够被外部访问的部分,实际上主要指的是public和published的部分。
假定有一个类,其中有很多子程序,有用来操作命令栈的,有用来格式化报表的,有用来打印报表的,还有用来初始化全局数据的。在命令栈,报表和全局数据中很难看出有什么联系。类的接口不能展现出一种一致的抽象,因此类的内聚性就很弱。这告诉我们,不要以为创建了一个类就万事大吉了,如果内聚性不高,这样的类也很失败,特别是随着代码增多、功能更复杂,类的内聚性,可读性都会大打折扣。
类的接口应该展现一致的抽象层次
每一个类应该实现一个ADT,并且仅实现这个ADT。如果你发现某个类实现了不止一个ADT,或者你不能确定它究竟实现了何种ADT,你就应该将这个类重新组织为一个或者多个定义更加明确的ADT。一个类实现的抽象数据类型太多,代码会难以理解,并且数据之间的耦合会增加。
提供成对的服务
大多数操作都有和其相对应的、相等的及相反的操作。这就好比在Delphi中创建一个对象就必须有一个对应的释放操作。
把不相关的信息转移到其它的类中去
尽可能让接口可编程,而不是表达语义
谨防在修改的时候破坏接口的抽象
不要添加与接口抽象不一致的公用成员
同时考虑抽象性和内聚性
尽可能地限制类和成员的可访问性:让可访问性低是促成封装的原则之一。当你犹豫某个子程序应该是public,protected还是private的时候,经验之举就是采用最严格的访问级别。
不要公开暴露成员数据:暴露成员数据会破坏封装性,从而限制你对这个抽象的控制能力。
避免把私用的实现细节放入类的接口中
避免使用友元类:友元会破坏封装性,它让你不得不同时考虑很多代码。所以应该尽量少用友元类,这样代码的复杂度会下降。
让阅读代码比编写代码更方便:如果必须看到底层实现才能理解发生的事情,那还算不上抽象。
留意过于紧密的“耦合”关系:“耦合”指的是两个类之间关联的紧密程度,通常这种关联越松越好。
(1)尽可能的限制类和成员的可访问性
(2)避免友元类,因为它们是紧密耦合的。
(3)在基类中把数据声明为Private而不是protected,以降低派生类和基类之间的耦合程度。
(4)避免在类的公开接口中暴露成员数据
3.有关设计和实现的问题
包含(“有一个…”的关系):包含是一个简单概念,表示一个类含有一个基本数据元素或对象。继承比包含更复杂,更容易出错。包含是面向对象的主力技术。
继承(“是一个…”关系):继承的概念是说一个类是另一个类的特殊化。除非派生类真的“是一个”更特殊的基类,否则不应该从基类继承。
(1)用Public继承来实现“是一个…”的关系
(2)不要覆盖一个不可覆盖的成员函数:派生类中的成员函数名不要跟基类中不可覆盖的成员函数重名。
(3)只有一个实例的类是值得怀疑的:说明设计中将类和对象混为一谈了。
(4)只有一个派生类的基类也是值得怀疑的
(5)派生后覆盖了某个子程序,但在其中没做任何操作,这种情况也值得怀疑
(6)避免让继承体系过深:7+-2,过深的继承层次使得代码的错误率显著增长,增加代码复杂度。这就跟继承本身的使命背道而驰。
(7)尽量使用多态,避免大量类型检查
(8)让所有数据都是private而不是protected:如果派生类真的需要访问基类中的成员数据,可以采用提供protected访问器寒素。
成员函数和数据成员
(1)让类中子程序数尽量少
(2)对其它类的子程序的简介调用尽可能少,要避免出现A.B.C甚至A.B.C.D的情况。
构造函数
(1)如果可能,应该在所有的构造函数中初始化所有数据成员:这样的好处是避免在赋值之前就调用某个成员变量。
(2) 优先采用深拷贝
要避免的类:
(1)万能类:什么都知道,什么都能干
(2)消除无关紧要类:只包含数据而不包含行为或者只包含行为而不包含数据的类都应该避免。