《代码大全》阅读笔记 第六章 可以工作的类
整章讲述了如何编写出高质量的类。
文章的开头部分,讲述了计算机时代的发展史:
1. 程序员基于语句思考编程问题
2. 程序员基于子程序思考问题
3. 程序员基于类思考问题。估计下一阶段就是基于包思考问题了。
6.1 类的基础:抽象数据类型
什么是类?
一组数据与子程序构成的集合,这些数据与子程序共同拥有一组内聚的、明确定义的职责。
类,是一个抽象的数据类型。
关于类和抽象数据类型的概念,可以查看JAVA的面向对象的理念。
抽象数据类型的好处:
1. 可以隐藏实现的细节,保护其他数据和子程序不受影响。
2. 改动不会影响整个程序:类是一个相对独立的个体,虽然与其他的类有联系,存在交互的关系。但是修改一些细节(比如,私有变量,私有子程序)的时候,是不会影响到整个程序的。
3. 让接口提供更多的信息:这一点有点勉强。意思是一个变量或者一个常量,单独看,得不到什么具体的信息,但是将它与包含它的类结合起来,就可以得到更多的信息。
4. 更容易提高效率:当希望修改某一个算法,或者逻辑处理的时候,只需要修改类当中的某一个子程序就可以,无需修改整个程序,大大的提高效率。
5. 程序更具有自我说明性:良好的类,可以通过给变量和子程序良好的命名方式,可以很方便的帮助人们认识它们。
6. 无需在程序内传递数据:类内部很容易拥有数值共享的能力,其他的子程序操作的时候,可以直接使用。
7. 你可以像在现实世界中那样操作实体,而不用在底层实现操作它。
抽象数据类型是构成类/class的概念的基础。
6.2 良好的接口
创建高质量类,最重要的是为这个类创建好的接口。接口可以帮助类展现合理的抽象,并确保细节仍然被隐藏在抽象背后。
好的抽象
什么是抽象:抽象是以简化的形式来看待复杂操作的能力。只找出复杂事务中,我们需要关注的部分,通过类去描绘这些部分。这个过程就是抽象。
如何编写好接口的建议:
1. 类的接口应该展现一致的抽象层次
为那些接口提供分组,比如将相关返回值的放在一起,比如将相同操作对象的放在一起等等。
2. 一定要理解类所实现的抽象是什么
明确目标,根据需要,设定接口。
3. 提供成对的服务
大多数操作都是相对的,比如开灯操作和关灯操作。当然不需要盲目的创建相反操作,只需要根据需要决定,是否真的需要与不需要。
4. 把不相关的信息转到其他的类中
5. 尽可能让接口可编程,而不是表达语义
一个接口通常包含可编程部分和语义部分,可编程部分是有数据与逻辑算法组成,语义部分是指通过注释和变量名称来描述自己应该如何被使用。
6. 谨防在修改是破坏接口的抽象
避免修改部分代码的时候,影响到原先的接口方法。
7. 不要添加与抽象不一致的公用成员
8. 同时考虑抽象性和内聚性:高内聚
良好的封装
比较抽象的概念,通过忽略实现细节的形式来降低项目的复杂度。
实现良好封装的建议:
1. 尽可能的限制类和程序变量的可访问性。
2. 不要公开的暴露成员数据,容易限制你对这个抽象的控制力。不是很明白,可能的道理是,当你知道的越多的时候使用它,就会顾虑很多方面的因素。
3. 避免把私有数据放入类的接口中,这样会暴露类的实现细节。真正的封装之后,程序员是不会看到任何实现细节的。
4. 不要对类的使用者做任何的假设。当设计和实现出来的接口必须建立在某个假设或者某个如果的基础上的话,是不够完善的。
5. 不要因为一个子程序仅使用公用子程序就把它归入公开接口。可能的理解是,当你在设定某个子程序是否为公开接口的时候,不仅要考虑它是否仅使用公用子程序,还需要考虑公开此接口的时候,是否符合抽象的一致性。
6. 要格外警惕从语义上破坏封装性。不懂,真心不懂咯。
7. 留意过于紧密的耦合关系。以前一直听人说:高内聚低耦合,类的内部子程序之间需要高内聚,类与类之间,模块与模块之间需要低耦合。紧密的耦合关系会使得它们容易发生牵一发而动全身的后果。
6.3 关于设计与实现的问题
包含----(has a 有一个的关系)
实现包含的几条建议:
1. 在万不得已的时候通过private继承来实现“有一个”的关系
2. 警惕有超过7个数据成员的类。包含的东西太多,不利于管理,提高了复杂度。
继承(is a 有一个的关系)
什么是继承,简单的说,根据一个已经存在的类里面的内容来创建一个新的类。父子关系。
在使用继承之前,需要考虑的事宜:
对于每一个成员变量来说,它们应该对派生类可见吗?它应该有默认的实现方式吗?可以被覆盖override吗?
每个人都有隐私的数据,作为父亲的基类,并不是所有东西都与作为儿子的派生类共享的。这里面为了降低派生类的复杂度,应该考虑派生类是否真的需要。
书本中对继承使用的几点建议:
1. 勇猛使用继承并进行详细的说明,要么就不要用它。 继承是一个危险的技术,增加了程序的复杂度。
2. 遵循Liskov替换原则。“除非派生类是一个’更特殊’的类,否则不应该从基类继承”。这里的更特殊是指,派生类与基类在抽象上具有一致性,并且派生类在抽象上更具升华。
合理的使用继承,可以带来好处,不合理则相反。
3. 不要覆盖一个不可覆盖的成员函数。因为你覆盖的时候,创建出来的成员函数与基类同名的成员没有一丁点关系。
当基类的成员函数是私有属性的时候,它是允许派生类创建一个同名同参的方法的。
4. 把公用的接口、数据及操作放到继承树中尽可能高的位置。
继承树是什么?继承树可以理解为通过父类与他的子孙类形成的树状关系图。树的顶端是最初的基类。
站的越高,看的越远。此处,放的越高,越多的子孙可以萌祖阴。
5. 只有一个实例的类是值得怀疑的-----虽然知道单例,但是作者的几句对问题的描述不怎么懂。
6. 只有一个派生类的基类也值得怀疑
7. 派生后覆盖了某个子程序,但是在其中没做任何操作,这种情况也值得怀疑。
8. 避免继承的体系过深
继承的太多,不方便管理,提高了程序的复杂度。
9. 尽量使用多态,避免大量的类型检查。 不懂~~
10. 让所有的数据都是private。这个说的有点绝对,提醒我们注意派生类是否真的需要基类的某一个方法。
成员函数与数据成员
成员函数与数据成员方面的几点建议:
1. 让类中的子程序数量尽可能的少。
2. 禁止隐式地产生你不需要的成员函数和运算符
3. 减少类调用其他类中不同子程序的数量
4. 对其他类的子程序的间接调用要尽可能的少
5. 一般来说,尽可能减小类和类之间相互合作的范围
构造函数
几点建议:
1. 如果可能,应该在所有的构造函数中初始化所有的数据成员
2. 用私有private构造函数来强制实现单例模式
3. 优先采用深层复本,除非论证可行,采用浅层复本。 不懂~~,在程序中把对象复制出副本,好像没见过,在想是不是通过一个变量把一个通过困难重重得来的对象保存起来,为下次调用的时候无需重复执行困难重重的过程,只需调用即可。
6.4 创建类的原因
本节讲述了创建类的意义,为什么需要类,创建类的好处
1. 为现实世界的对象建模。面向对象的味道
2. 为抽象的对象建模。面向对象的味道
3. 降低复杂度。类可以把不需要的信息,实现功能的具体细节都隐藏起来,只给出必须的接口,使得我们无需去考虑那些繁琐的细节。
4. 隔离复杂度。
5. 隐藏实现细节。
6. 限制变动的影响范围。通常类与类之间是比较独立的,耦合性比较低的。
7. 隐藏全局数据。不是很明白
8. 让参数的传递更顺畅。
9. 建立中心控制点。通过一个类来控制整个程序,或者整个模块。
10. 让代码更易于通用。
11. 把相关操作绑在一起。
12. 实现某种特定的重构。这个在以后的章节详细描述。
应该避免的类
1. 避免创建万能类
2. 消除无关紧要的类
3. 避免用动词命名的类
6.5 与具体编程语言相关的问题
不同的编程语言,在类的设计与实现过程中会存在一些或多或少的差异,需要明确认识。
6.6 超越类:包
从语句到子程序,到类,相信下一个就是包。
类是当前程序员实现模块化的最佳方式。…….有点模糊,觉得作者好像什么都没说。其实在编程的时候,大多数使用包来分隔模块的。
写在最后:
这一章讲诉了很多的规则,就跟中学里面的化学方程式似的。
一章读下来,都的过程中有很多东西是模糊的,甚至不懂。可能是我还菜鸟,希望我第二遍读这本书的时候,能够没有这些模糊的未知的。
TAG: