Delphi面向对象学习随笔四:继承与封装
CnPack 开发网站 2008-06-21 07:59:22
Delphi面向对象
学习随笔四:继承与封装
作者:巴哈姆特
(转载请注明出处并保持完整)
在讨论类的封装前,我想先说说“继承”和“包含”的区别。
继承(是一个(is a ...)):
我在看很多资料的时候,讲到类的继承时,很多资料都会提到:“选一个合适的类做为新类的父类可以有效的提高代码的重用程度,从而减少很多重复的工作量。”
的确,上面的话很有道理,合理的选择父类是可以减少重复的工作,但是,这里存在一个逻辑性的问题:即、新的子类“是一个(种)”父类,这么说或许有些绕口,举个例子:例如动物类是生物类的子类,那么我们也可以这样说:“动物是一种生物。”这样说,显然在逻辑上并没有矛盾的地方。
那么,我们来看下一个例子:
type
TEmployee = class(TStrings)
...
public
property EmployeeName: string ...; //员工
property EmployeeAge: Word ...; //年龄
....
property Strings.....; //考勤记录
end;
这是我曾经看过的一个类,描述一员工的信息的,包括其考勤记录。首先这个类是没有语法和语义错误的,而且工作起来也正常,但是它在“继承”这个逻辑概念上却有问题,那就是“员工/职员 是 一个列表,”这样说显然不合情理。
我曾经问过作者这样设计的意图,他回答只是“这样简单,可以运行就得了,管那么多做什么?”但是,我认为,这样是错误的,因为在逻辑上说不通。
包含(有一个(has a ...))
包含其实就是指一个类包含另一个类的实例,比如
Delphi中的TCombobox的Items属性。
上面的类,我们可以这样改:
type
TEmployee = class(TObject)
...
public
property EmployeeName: string ...;
...
property Attendances: TStrings ...; //考勤记录
end;
这样看看是不是好多了呢,而且在逻辑上就从“员工是一个
列表”变成了“员工有一个考勤列表”。
类的封装:
在决定编写一个新类前,我们首先应该仔细考虑这个类是用来干吗的,而不是兴奋的开始写代码。一个良好的类的接口(属性或方法,和interface是两个概念,以下统称接口,懒得打那么多字,^_^ 或许后面会说interface的概念)应该是具有一致性的。
假如有一个类,他的一部分接口用来操作堆栈,一部分接口用来从数据库中提取数据,还有一部分接口用来控制打印走纸,那么,这个类看上去真的比较乱,因为用户不知道你这个类到底是为什么编写的,这样的类也就不能称之为类了。看上去更像是把一堆风马牛不相及的函数死拉硬拽到一起“凑
份子”凑出来的“ 累”。
一个封装良好的类,应该有一个一致性的接口,也就是说,类提供给外界使用的所有接口都应该是有关联的,上面的类其实我们可以写成三个类比较好:一个类用来操作数据库、一个类用来操作堆栈、一个类用来控制打印机走纸。
这样
设计的好处是能让人一看就清楚的知道,你这个类到底是围绕什么来工作的,就像我们写作文也必须有一个中心思想一样,如果没有中心思想,那么这篇作文也就不叫作文了,而是流水帐了。
另外,友情提示一下,在任何时候,都不要有“写一个万能类”的想法。
隐藏信息:
在编写类的时候,我们应该非常清楚的知道,这个类有哪些信息是不需要外界访问的,就像司机开汽车,司机只需要知道怎么弄方向盘,怎么踩离合器,怎么刹车和提速就可以了。而有关汽车内部发动机的
工作原理,司机并不需要知道。
当我们在拿到一个类的CODE的时候,除非你是为了学习,否则你应该忍住看私有域所有实现代码的冲动。虽然现在提倡开源,但是我想开源的目的是为了提高相互交流,而并不是为了让你可以把原本私有的、外界无法访问的接口对外开放。如果你把原先外界无法访问的域信息