首页 > 代码库 > 面向对象的三个基本特征

面向对象的三个基本特征

面向对象的三个基本特征是:封装、继承、多态。
o_OOBase.gif
封装
封装最好理解了。封装是面向对象的特征之中的一个,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,而且类能够把自己的数据和方法仅仅让可信的类或者对象操作,对不可信的进行信息隐藏。
继承
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它能够使用现有类的全部功能,并在无需又一次编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,能够通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类能够继承多个基类。可是普通情况下,一个子类仅仅能有一个基类,要实现多重继承,能够通过多级继承来实现。
 
继承概念的实现方式有三类:实现继承、接口继承和可视继承。
Ø         实现继承是指使用基类的属性和方法而无需额外编码的能力;
Ø         接口继承是指仅使用属性和方法的名称、可是子类必须提供实现的能力;
Ø         可视继承是指子窗口(类)使用基窗口(类)的外观和实现代码的能力。
在考虑使用继承时,有一点须要注意,那就是两个类之间的关系应该是“属于”关系。比如,Employee 是一个人,Manager 也是一个人,因此这两个类都能够继承 Person 类。可是 Leg 类却不能继承 Person 类,由于腿并非一个人。
抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用keyword Interface 而不是 Class
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
 
多态
多态性(polymorphisn)是同意你将父对象设置成为和一个或很多其它的他的子对象相等的技术,赋值之后,父对象就能够依据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:同意将子类类型的指针赋值给父类类型的指针。
实现多态,有二种方式,覆盖,重载。
覆盖,是指子类又一次定义父类的虚函数的做法。
重载,是指同意存在多个同名函数,而这些函数的參数表不同(也许參数个数不同,也许參数类型不同,也许两者都不同)。
事实上,重载的概念并不属于“面向对象编程”,重载的实现是:编译器依据函数不同的參数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这种)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这种:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是“覆盖”。当子类又一次定义了父类的虚函数后,父类指针依据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这种函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这种函数地址是在执行期绑定的(晚邦定)。结论就是:重载仅仅是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce Eckel的话:“不要犯傻,假设它不是晚邦定,它就不是多态。”
那么,多态的作用是什么呢?我们知道,封装能够隐藏实现细节,使得代码模块化;继承能够扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现还有一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
 
概念解说
泛化(Generalization
o_Generalization.gif
图表 1 泛化
在上图中,空心的三角表示继承关系(类继承),在UML的术语中,这样的关系被称为泛化(Generalization)。Person()是基类,Teacher(教师)Student(学生)Guest(来宾)是子类。
若在逻辑上B是A的“一种”,而且A的全部功能和属性对B而言都有意义,则同意B继承A的功能和属性。
比如,教师是人,Teacher Person的“一种”(a kind of )。那么类Teacher能够从类Person派生(继承)。
假设A是基类,B是A的派生类,那么B将继承A的数据和函数。
假设类A和类B毫不相关,不能够为了使B的功能很多其它些而让B继承A的功能和属性。
若在逻辑上B是A的“一种”(a kind of ),则同意B继承A的功能和属性。
 
聚合(组合)
o_aggregationBase.gif
图表 2 组合
若在逻辑上A是B的“一部分”(a part of),则不同意B从A派生,而是要用A和其他东西组合出B。
比如,眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类Eye、Nose、Mouth、Ear组合而成,不是派生(继承)而成。
 
聚合的类型分为无、共享(聚合)、复合(组合)三类。
 
聚合(aggregation
 
o_aggregation.gif
图表 3 共享
上面图中,有一个菱形(空心)表示聚合(aggregation)(聚合类型为共享),聚合的意义表示has-a关系。聚合是一种相对松散的关系,聚合类B不须要对被聚合的类A负责。
 
组合(composition
o_composition.gif
图表 4 复合
这幅图与上面的唯一差别是菱形为实心的,它代表了一种更为牢固的关系——组合(composition)(聚合类型为复合)。组合表示的关系也是has-a,只是在这里,A的生命期受B控制。即A会随着B的创建而创建,随B的消亡而消亡。
 
依赖(Dependency)
o_Dependency.gif
图表 5 依赖
这里BA的关系仅仅是一种依赖(Dependency)关系,这样的关系表明,假设类A被改动,那么类B会受到影响。

 

 

UML对非常多人来说应该不是一个陌生的概念,这一两年来,UML被大家越来越多的讨论着。本来UML跟我这个主题似乎并不能扯上多大的关系(它是语言无关的,甚至能够说其本身就是一种语言——用于交流的)。我在此谈到它有两个目的:
1. UML是针对面向对象软件开发的,而C++正是这种一种语言
2. UML在设计中被越来越多的使用着,而下一篇杂谈准备讨论设计模式,假设不了解UML,那么无法进行下去

UML,全称:Unified Modeling Language,其目的是为了对软件密集型的制品进行可视化、详述、构造和文档化的图形语言。UML是根据很多前人的思想总结出的成果,1997年被OMG通过并成为标准(所以在《设计模式》书中假设你看到与标准不一样的地方,不要奇怪,那本书是95年的)。关于UML的历史和更具体的描写叙述,能够參考《UML 參考手冊》。UML主要由一系列视图组成,当中包含静态视图(Static view),用例视图(Use case view)活动视图(Activity view)等,不同的图用处自然也不一样,而对开发者来讲(或者说为我的下一篇来说),更重要的应该是静态视图中的类图(class diagram)和交互视图(Interaction view)中的顺序图(Sequence diagram),请注意view和diagram的差别。

类图
静态视图说明了对象的结构,当中最经常使用的就是类图,类图能够帮助我们更直观的了解一个系统的体系结构,有时侯,描写叙述系统快照的对象图(Object diagram)也是非常实用的。在这里,我们主要介绍类图,以下的图就是一个简单的类图:

在类图中,类由矩形框来表示,如上图中,定义了4个类,分别为Base、A、B、C,类之间的关系通过各种线条和其它符号来表示,在上图中,空心的三角表示继承关系,在UML的术语中,这样的关系被称为泛化(Generalization),所以上面的类用等价代码表示为:
class Base{…};
class A:public Base{…};
class B:public Base{…};
class C:public Base{…};

我们再看下一幅图:

这幅图与上幅差点儿没有什么差别,唯一的不同就是Base类中添加了成员,一个私有的integer _x(UML术语为property)和一个公有的fun()的函数(method),是否须要这些类的内部细节UML本身并没有限制,全然取决于你自己怎样使用,UML的用处在于帮助你了解系统,所以仅仅要你自己认为足够清楚,那么够了,不要再复杂了。
接着看第三幅图:

上面图中的箭头表示一种关系,箭头还有一边有一个菱形(空心)表示聚合(aggregation),聚合的意义表示has-a关系,其等价代码例如以下:
class A{…};
class B{ A* theA;…};
聚合是一种相对松散的关系,聚合类B不须要对被聚合的类A负责。
以下的图:

这幅图与上面的唯一差别是菱形为实心的,它代表了一种更为牢固的关系——组合(composition)。组合表示的关系也是has-a,只是在这里,A的生命期受B控制,通常情况,等价代码例如以下:
class A{…};
class B{A theA;…};
即A会随着B的创建而创建,随B的消亡而消亡。

下图:

这里B与A的关系仅仅是一种依赖关系,这样的关系表明,假设类A被改动,那么类B会受到影响,一个简单的样例就是:
class A{…};
class B{fun(A params);…};

经常使用的关系就是我们上面用的这些,通过这些关系和类表示的类图,我们能够用图形化的方式描写叙述一个系统的设计部分,当你习惯使用UML后,你会发现,这往往比你告诉同伴某某类从某某类派生,派生类又和某某类具有什么关系easy的多。

顺序图:
UML中另外一个经常使用的图形就是交互视图中的顺序图,在以往的过程化语言中,我们通常使用流程图来描写叙述一个函数(系统)是怎样工作的,而在面向对象的系统中,这显然是不可行的,而顺序图正是来解决问题的。
如果有例如以下的伪代码:

class circle{
public:
void fillcolor()
{
// ...
}
void draw()
{
fillcolor();
}
};
class window{
public:
void drawcircle()
{
_circle.draw();
}
private:
circle _circle;
};

对于以下的调用:
window wnd;
wnd.drawcircle();
相应的顺序图例如以下:

 

图中上方的方块表示參与的对象,垂直的虚线表示对象的生命线,方框表示激活,当中箭头表示了一个调用消息(也能够有回送return),假设是异步的消息,则用半箭头表示,当中draw表示了一个自调用(self call)

至此,UML中最经常使用的(从开发者的角度),当然UML的内容远远不仅仅这些,这里的介绍仅仅是一些简单的概括,并且UML本身也在不断的发展之中,不管如何,我认为UML会越来越多的深入我们的开发过程中,特别是对下一篇我们要介绍的设计模式而言,类图是基本的描写叙述工具(到那个时候你会体会到UML描写叙述的优越)。
假设你看过《设计模式》着本书,你会发现与我上面所描写叙述的有一些细微的不同,不要紧张,《设计模式》是GOF95年的作品,那时候UML还没有形成,并且,当中也明白那是OMT方法(Jim Rumbaugh在通用电气发表的建模技术——Object Modeling Technique)和Booch方法。假设你认为UML有些让你无所适从,也不必紧张,UML本身仅仅是一个辅助工具,它的目的是帮助你描写叙述系统,不是复杂你的工作,假设你的系统非常easy,一句话能够说的非常清楚,那么不要用UML,假设你仅仅想说明类之间的关系,而不是类的接口描写叙述,那么像第一副图那样简单的描写叙述就非常好,总之不要去追求细节,仅仅要能说明问题,那么你的目的就达到了(甚至你没有必要全然遵守规范)。

參考书目
《设计模式——可复用面向对象软件的基础》
作者: Erich Gamma 等 译者:李英军 马晓星 蔡敏 刘建中 机械工业出版社2000

《UML參考手冊》作者: James Rumbaugh Ivar Jacobson Grady Booch
译者: 姚淑珍 唐发根 机械工业出版社2001

《UML精粹——标准对象建模语言简明指南(第2版)》
作者: Martin Fowler&Kendall Scott 译者: 徐家福 清华大学出版社 2002

《Design Patterns Explained》Alan Shalloway, James R. Trott
Addison-Wesley Pub 2001

面向对象的三个基本特征