代码大全学习-9-实现

接口设计好了,实现也不能含糊。下面就来讲实现相关的问题,包含,继承,成员变量和成员函数,构造函数等。

包含

先说包含与继承。包含是“有”(has a)的关系,继承是“是”(is a)的关系。这还是很好区分的。包含也可以通过私有继承来实现,但这种方式应该是实在不得已才为之的,因为这样做会破坏封装,增加复杂性,不提倡。无论是包含还是继承,都要注意一个著名的数字7±2。这个数字号称是一个人能同时记住的任务数量,人也是一个多线程的系统,这个数字可以看做是人这个操作系统的最大活动线程数,太多了,要么让它去休眠,要么可能导致内存不够用,系统运行容易出错。对于包含来说,就是一个类的成员变量不要超过7±2个。太多了就说明这个类太复杂了,可能要分成几个。个人非常同意这个观点,那些动不动就几十个成员变量的类经常是看到后面忘了前面,看了半天还不知道到底它要干什么。对于继承来说,一是继承的层级不要超过这个数,太多了就晕了;二是最好一个类的子类总数也不要超过这个数,多了也记不清。其实这个原则可以用到很多地方,不光是写程序,生活中也可以应用。总而言之就是降低复杂性,系统运行维护就顺畅。

继承

继承是面向对象中非常强大的工具,然而它也是一把双刃剑,用的不好不仅不会降低复杂性,反而会增加复杂性。使用继承的时候,需要做一些决定,比如基类的成员函数是否对派生类可见,有没有默认的实现,默认的实现能不能被覆写(override),基类的数据成员(包括变量,常量,枚举等)是否对派生类可见等等。为了作出这些决定,有一些方法可以应用:

  1. 继承一定要保证“是”的关系。派生类一定是一个基类,可以增加接口,不能改接口,如果要改接口,就意味着在某种程度上不是“是”的关系,就要考虑其他的方法。
  2. 对于要被继承的类,一定要注释,要写文档,如果不是要被继承的类,就禁止它被继承。这样可以最大限度的降低复杂性。
  3. 应用里氏替换法则(Liskov Substitution Principle, LSP)。这个跟第一条其实差不多,就是要保证派生类能通过基类的接口去调用,而用户不需要知道它们的区别。也就是说在基类定义的接口在派生类里表示的要是同样的意思。
  4. 只继承你需要继承的那部分。这也是为了降低复杂性。
  5. 不要在派生类里重用基类中不能覆写的函数名。
  6. 一些通用的接口,数据,行为,尽量往上层提,提的越高,下面越简单,复杂性越低。
  7. 要留心那些只有一个实例的类(singleton模式除外),考虑是否可以直接用一个对象。
  8. 要留心那些只有一个派生类的基类,它真的是需要的吗?不要老是想着以后的需求,很多时候我们当时对以后的需求都理解有误,而这样提前设计会增加复杂性。最好的为以后准备不是多设计一些层为以后用,而是尽量保证目前的代码简单,整洁,清晰,直接,以后要改的人一看就懂。这一条是我们常犯的错误,但有时候也没那么绝对,不要太教条主义。
  9. 要留心那些覆写了一个函数却在那里面什么都不做的类。通常这意味着抽象层级出了问题,要重新考虑抽象合不合适。
  10. 不要弄出太深的继承数,如前面所说,太多了记不住,难理解,易出错。
  11. 尽量用多态而不是很多类型检测。比如一大串的case,case圆形,怎样;case方形,怎样;case三角形,怎样。这样的最好用继承来解决。当然,要确实能抽象出有意义的表示才用,比如前面的例子可以抽象出形状,否则看的不明不白还不如直接用case。
  12. 让所有的数据都是私有的。
  13. 对于多重继承,更要特别注意,最好是只在增加一些属性的时候使用。像Java就规定了多重继承只能有一个类,其他的都必须是接口。

为什么继承有这么多规矩呢,根本原因就是一个,不能让继承增加系统的复杂性。

成员

再说说成员变量和成员函数。也有一些指导性的方法:

  1. 一个类里面的成员函数越少越好,多了会增加复杂性。当然,也要考虑不要因此弄出太多的类,太深的继承层次,要做一个平衡。
  2. 可以通过把一个函数定义成私有的来禁止一些不想被类的用户访问的功能,比如赋值,构造等。
  3. 不要让一个类调用太多的类。
  4. 尽量不要间接调用其他的类。这里有著名的迪米特法则(Law of Demeter)。如果一个对象A里实例化了一个对象B,A可以调用B的函数,但是要尽量避免调用B里面的其他对象的函数。就是在A里面用B.fun()是可以的,用B.C.fun()就不好了。其实也就是要让层次清晰,如同管理一样,不要越级打报告,要找直接领导。

构造函数

然后说说构造函数的注意事项:

  1. 如果可能的话,尽量在构造函数中初始化所有的成员。
  2. 如果一个类只有一个实例,就把它的构造函数设成私有的。
  3. Copy的时候能传值就传值,不要传址,除非是证明传值实在不满足需求。因为传址更容易引起错误。

其实方法不止列举的这些,而列举的这些也不是金科玉律,很多时候还是要我们自己多想一想,灵活运用。

接着,阐述我们为什么要创建类。原因多种多样:对真实世界的对象建模;对抽象对象建模;降低复杂性;隔离复杂性;隐藏细节信息;限定变化的范围;隐藏全局数据;流水线式的数据传递;让每个任务都只有一个控制点;方便代码重用;为一簇程序做准备;打包相关的操作;实现重构等等。好处很多,核心还是降低软件的复杂性。

打包

最后,类创建好了,太多了不好管理怎么办?分类打包。像Java就已经支持package了,对于不支持的编程语言,可以自己制定规则遵照执行,效果其实也一样。一个包可以看做是一个子系统或者叫building block。也是软件发展的一个必然结果。由汇编构成高级语言的一条条语句,这一条条语句构成了一个个函数,一个个函数构成了类,一个个类组成了包。以后可能还有更多层包,不过层次太多,人又会晕了,那个神奇的数字:7±2。

附上第六章的checklist:
 

Checklist: Working Classes

Abstract Data Types

  • Have you thought of the classes in your program as Abstract Data Types and evaluated their interfaces from that point of view?

Abstraction

  • Does the class have a central purpose? 
  • Is the class well named, and does its name describe its central purpose? 
  • Does the class's interface present a consistent abstraction? 
  • Does the class's interface make obvious how you should use the class? 
  • Is the class's interface abstract enough that you don't have to think about how its services are implemented? Can you treat the class as a black box? 
  • Are the class's services complete enough that other classes don't have to meddle with its internal data? 
  • Has unrelated information been moved out of the class? 
  • Have you thought about subdividing the class into component classes, and have you subdivided it as much as you can? 
  • Are you preserving the integrity of the class's interface as you modify the class? 

 Encapsulation

  • Does the class minimize accessibility to its members? 
  • Does the class avoid exposing member data? 
  • Does the class hide its implementation details from other classes as much as the programming language permits? 
  • Does the class avoid making assumptions about its users, including its derived classes? 
  • Is the class independent of other classes? Is it loosely coupled?

 Inheritance

  • Is inheritance used only to model "is a" relationships? 
  • Does the class documentation describe the inheritance strategy? 
  • Do derived classes adhere to the Liskov Substitution Principle? 
  • Do derived classes avoid "overriding" non overridable routines? 
  • Are common interfaces, data, and behavior as high as possible in the inheritance tree? 
  • Are inheritance trees fairly shallow? 
  • Are all data members in the base class private rather than protected?

 Other Implementation Issues

  • Does the class contain about seven data members or fewer? 
  • Does the class minimize direct and indirect routine calls to other classes? 
  • Does the class collaborate with other classes only to the extent absolutely necessary? 
  • Is all member data initialized in the constructor? 
  • Is the class designed to be used as deep copies rather than shallow copies unless there's a measured reason to create shallow copies?

 Language-Specific Issues

  • Have you investigated the language-specific issues for classes in your specific programming language? 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值