代码大全2 第二章创建高质量代码(高质量类)

类:

是由一组数据和子程序构成的集合,这些数据和子程序共同拥有一组内聚的、明确定义的职责。而类的基础则是抽象数据类型。
(最开始认识类的时候我们就知道,类有三大特性:继承、封装、多态,也有把抽象当作一大特性变成四大特性的,我比较认同的是三大特性的说法,《代码大全》中说类是数据和子程序的集合,考虑类的一种方式就是把它看作是抽象数据类型加上继承和多态两个概念,抽象这一特性在继承、封装、多态中是密不可分的)

抽象数据类型:

指一些数据以及对这些数据所进行的操作的集合。

不懂抽象数据类型的程序员开发出来的类只是名义上的“类”而已------实际上只不过就是把一些稍有点关系的数据和子程序堆在一起。然而理解抽象数据结构之后,程序员就能写出一开始很容易实现、日后也易于修改的类来

使用抽象数据类型有着很多好处,在《代码大全2》这本书中使用了很多例子来描述在面向对象和非面向对象中使用抽象数据类型的好处。我看完这部分更多的认识还是在“抽象”上,不管是数据还是子程序,合理恰到的抽象,会让程序的流程更加流畅,当你在面向对象环境中能够更好的处理继承、封装、多态的关系,建议阅读原文(126-133页),如果你善于使用面向对象思维编写代码,肯定会很有体会。

良好的类接口

创建高质量的类,第一步可能也是最重要的一步,就是创建一个好的接口。这也包括了创建一个可以通过接口来展现的合理的抽象,并确保细节仍被隐藏在抽象背后。
类的接口应提供一致的抽象。因为接口中的每个子程序都在朝着这个一致的目标而工作。一个没有经过良好抽象的类可能包含大量混杂的函数,会导致内聚性很弱,这可能比较不好理解其一致性是怎么样的,如果换一句换理解**“一个函数应该只做一件事”**,类的接口也是类似的,它应该是职能专一的。
为了追求设计优秀,《代码大全》给出了一些创建类的抽象接口的指导建议:

  • 类的接口应该展示一致的抽象层次(举一个反例,有一个汽车的抽象类,结果该类中出现**“汽车人,变形”**,其它的自己体会了)
  • 一定要理解类所实现的抽象是什么(抽象可能就是模糊的,会存在两种相似的抽象,要确保自己理解的是正确的)
  • 提供成对的服务(有新增就有删除,这就是成对服务,这并不是盲目遵守的,而是要确定是否一定需要)
  • 把不相关的信息转移到其它类中
  • 尽可能让接口可编程,而不是表达语义(可编程部分是在程序编译时就能检查错误的,而语义部分则是无法通过编译器检查错误的,比如果一个接口调用时必须先调用另外一个接口才可以正常运行,这就是语义接口,它无法通过编译器强制实施,是一个可能被误用的部分,或许可以理解为无法在编译时发现,必须在运行时才能发现问题的接口)
  • 慎防在修改时破坏接口的抽象
  • 不要添加于接口抽象不一致的公用成员
  • 同时考虑抽象性和内聚性

良好的封装

封装时一个比抽象更强的概念。抽象通过提供一个可以让你忽略实现细节的模型来管理复杂度,而封装则强制阻止你看到细节,没有封装,抽象往往很容易被打破。

良好的封装需要达到以下条件:

  • 尽可能的限制类和成员的可访问性
  • 不要公开暴露成员数据
  • 避免把私用的实现细节放入类的接口中
  • 不要对类的使用者做出任何假设
  • 避免使用友元类
  • 不要因为一个子程序里仅使用公共子程序,就把它归入公开接口
  • 让阅读代码比编写代码更方便
  • 要个格外警惕从语义上破坏封装性
  • 留意过于紧密的耦合关系
    • 尽可能的限制类和成员的可访问性
    • 避免友元类,因为他们之间时紧密耦合
    • 在基类中把数据声明为private而不是protected,以降低派生类和积累之间的耦合程度
    • 避免在类的公开接口中暴露成员数据
    • 要对从语义上破坏封装性保持警惕
    • 察觉“Demeter(德墨忒耳)法则”

有关于设计和实现的问题

给类定义合理的接口,对于创建高质量程序起到了关键作用,然而,类内部的设计和实现页也同样重要。

包含(“有一个…” 的关系)

包含表示一个类含有一个基本数据元素或对象。与包含相比,关于继承的讨论要多得多,这是因为继承需要更多的技巧,而且容易出错,而不是因为继承要比包含好。包含才是面向对象编程的主力技术。

  • 通过包含来实现Z“有一个/has a ”的关系
  • 在万不得已时通过private继承来实现 有一个 的关系
  • 警惕有超过约七个数据成员的类
继承(“是一个…" 的关系)

继承的概念时说一个类时另一个类的一种特性,目的是通过”定义能为两个或更多个派生类提供共有元素的基类“的方式写出更精简的代码。

如何用好继承:

  • 用public继承来实现”是一个…“的关系
    如果派生类不准备完全遵守由基类定义的同一个接口的契约,继承就不是正确的实现技术,请考虑用包含的方式,或者对继承体系的上层做修改

  • 要么使用继承并进行详细说明,要么就不要用它
    继承会给程序增加复杂度,因此它是一种危险的技术,要谨慎使用

  • 遵循Liskov替换原则
    派生类必须能通过基类的接口而被使用,且使用者无需了解两者之间的差异

  • 确保只继承需要继承的部分

  • 不要”覆盖“一个不可覆盖的成员函数
    派生类的成员函数不要与基类中不可覆盖的成员函数重名

  • 把共用的接口、数据集操作放到继承树中尽可能高的位置
    接口、数据和操作在继承体系中的位置越高,派生类使用他们的时候就越容易

  • 只用一个实例的类是值得被怀疑的
    只需要一个实例,可能表明设计中把对象和类混为一谈了

  • 只有一个派生类的基类也值得被怀疑
    只有一个派生类的基类大概率就是”提前设计“的产物,不要创建任何非绝对必要的继承结构。顺便提一句,不要做过多的提前设计,除非马上就会用到了

  • 派生后覆盖了某个子程序,但在其中没有任何操作,这种情况也值得怀疑

  • 避免让继承体系过深
    会增加大量复杂度

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

  • 让所有数据都是private(而非protected)

在什么情况下可以使用继承,什么情况又该使用包含:

  • 如果多个类共享数据而非行为,应该创建这些类可以包含的共用对象
  • 如果多个类共享行为而非数据,应该让它们从共同的基类继承而来,并在基类里定义共用子程序
  • 如果多个类及共享数据也共享行为,应该让他们从一个共同的基类继承而来,并在基类里定义共用的数据和子程序
  • 当你想由基类控制接口时,使用继承;当你想自己控制接口时,使用包含

继承是一种有用的工具,但是它会增加复杂度,这有违于软件的首要技术使用------管理复杂度,所以请谨慎使用。

成员函数和数据成员

指导意见:

  • 让类中子程序的数量尽可能的少
  • 禁止隐式的产生你不需要的成员函数和运算符
  • 减少类所调用的不同子程序的数量
  • 对其它类的子程序的间接调用要尽可能的少
  • 一般来说要尽量减少类和类之间相互合作的范围
    • 所实例化的对象的种类尽量少
    • 在被实例化对象上直接调用的不同子程序的数量尽量少
    • 调用由其它对象返回的对象的子程序数量尽量少

应该避免的类

  • 避免创建万能类
  • 消除无关紧要的类
  • 避免用动词命名的类
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值