面向对象之类的设计_代码大全笔记(二)

来源:互联网 发布:mac开机风扇声音很大 编辑:程序博客网 时间:2024/05/21 14:48

类的基础是抽象数据类型。抽象数据类型(ADT)是数据和对这些数据操作的集合。此“数据”为泛指,可以是窗体,文件,链表,汽车,飞机,人等。

类还支持继承和多态,因此可以认为,抽象数据类型 + 继承 + 多态 = 类

任何东西首先展示给人的都是外表,类需要一个良好的接口。(这里的接口主要是指类提供的public函数的集合)

1.合理的抽象

类的接口为隐藏具体实现而提供一种抽象,此接口应该提供一组明显相关的操作。--情非得已别让狗拿耗子

1.一致的抽象层

类设计时,把类看成是实现ADT的机制,每个类只实现一个ADT,若某个类实现了多个ADT,则应把这个类重新组织为多个明确的ADT。

如某个类中一半的子程序使用一部分数据,另一半的子程序使用另一部分的数据,此时可能已经把两个类混在一起了,可以考虑分开

2.理解类所实现的抽象

一些类非常相似,需要仔细理解类的接口,以确定所需要的抽象是哪个。

3.相反操作

开-关;添加-删除;激活-禁用等,设计类时,不要盲目创建相反操作,要认真考虑,是否需要。-做事情不要太顺手

4.抽象性和内聚性

好的抽象通常也有很高的内聚性,如果发现某个类的内聚性很弱,不知道如何修改,可以尝试看看这个类是否表现了一致的抽象。

2.良好的封装

抽象是忽略实现细节来管理复杂度,而封装是阻止看到细节。--非礼勿视

1.尽可能限制成员的可访问性

如果暴露一个子程序不会破坏抽象的一致性,则这么做是可行的,否则隐藏之会更好。

2.不要公开(public)数据成员

公开数据会破坏封装性,限制对抽象的控制能力。

3.避免把私有的实现细节放入类的接口中

可参考一中的隐藏抽象的实现细节一节的内容)把private段的内容放到类的头文件中还是暴露了部分的实现细节,可以把类的接口和类的实现隔离开来,在类的接口文件中声明一个指向类实现的指针。

4.避免使用友元

一般情况下友元会破坏封装性,有些场合如设计模式中,使用友元是为了管理复杂度。

5.让阅读代码比编写代码更方便

这个说起来容易做起来也容易,难的是一般程序员不屑于做。-这说到心坎上了

6.留意过于紧密的耦合关系(两个类之间关联)

以下是一些指导建议

  • 尽可能限制类和成员的可访问性
  • 避免友元类
  • 将基类数据声明为private而不是protected,以降低派生类和基类的耦合
  • 避免在类中暴露数据成员

3.类的设计和内部实现

包含(有一个的关系)才是面向对象编程的主力技术。-聚合优先于继承

1.万不得已时通过private继承来实现“有一个”的关系

某些情况下根本无法包含一个对象时,可以通过private继承来实现。-private继承实际是实现继承,相当于将基类的public和protected成员以private声明于子类

2.警惕有太多数据成员的类

研究表明,人们能记住的离散项目的个数是7±2。如果一个类有超过7个数据成员,可以考虑是否有必要分解为更小的类。如果数据成员是整形型者字符串这类简单数据类型,可以按7±2上限考虑,若是复杂对象,则按7±2下限考虑。-这个是否有点绝对化了,看到好多类都是超过这个数的,应该要视情况而言吧

 

当决定使用继承(是一个的关系)时,考虑如下

  • 对于每个成员函数,应该对派生类可见吗?应该有默认的实现吗?此默认实现可以被覆盖(override)吗?
  • 对于每个数据成员,应该对派生类可见吗?

1.public继承实现“是一个......”的关系

如果派生类不准备完全遵守基类定义的同一个接口契约,就不应该用继承。

考虑子类是否需要进行向上类型转换,如果不需要,那么继承就不是正确的实现技术。-这个很关键,不需要多态时应考虑聚合

2.遵循Liskov替换原则

3.派生类的成员函数不要与基类中不可覆盖的成员函数重名

如果一个函数在基类中是私有的,派生类就不要创建一个同名的成员函数。

4.公用的接口,数据放到继承树中尽可能高的位置

多高算高?当把子程序移到更高层次会破坏抽象性,就该停手。

5.派生类覆盖某个子程序,其中没做任何操作,这种情况需要注意

6.避免让继承体系过深

7.尽量使用多态,避免大量的类型检查

8.让所有数据都是private,而非protected

当决定使用多重继承之前,应该仔细考虑其他解决方案。

何时使用继承,何时使用包含

    • 多个类共享数据而非行为,应该创建这些类可以包含的共用对象
    • 多个类共享行为而非数据,应该从共同的基类继承,在基类定义共用子程序
    • 多个类同时共享数据和行为,应该从基类继承,并在基类中定义共用的数据和行为
    • 当需要用基类控制接口时,使用继承;当想自己控制接口时,使用包含。

成员函数和数据成员

  • 让类中子程序尽可能少
  • 禁止隐式地产生成员函数和运算符(类似将赋值运算符声明为private的方式
  • 减少类所调用的不同子程序的数量(扇入扇出概念
  • 对其他类的子程序的间接调用尽可能少

尽量减小类与类之前相互合作的范围

尽量让下面的数字最小

  • 所实例化的对象种类
  • 在被实例化对象上直接调用的不同子程序数量
  • 调用由其他对象返回的对象的子程序数量

4.创建类的原因

  • 为现实世界中的对象建模
  • 为抽象对象建模(经典的shape不是真实存在的)
  • 降低复杂度(信息隐藏,无需考虑他们了)
  • 隔离复杂度(隔离复杂算法,大型数据等)
  • 隐藏实现细节
  • 限制变动的影响范围
  • 隐藏全局数据
  • 让参数传递更顺畅
  • 建立中心控制点
  • 代码更易于重用
  • 为程序族做计划
  • 把相关操作包装到一起
  • 实现某种特定的重构

5.应该避免的类

  • 避免创建万能类
  • 消除无关紧要的类
  • 避免动词命名的类