在上一篇文章中,我简单地介绍了面向对象后面的哲学依据,在此基础上再去理解面向对象的编程思想就会简单得多。现在我们知道,这个世界,有概念和实体之分,概念是透过我们感官感知到的事物而进入我们的意识形成的,概念也可以认为是一种共相,代表某类事物拥有共同的属性和特征。我们通过分类的方法将这些概念进行组织,如此,我们人类便能够系统地区别不同的事物,并认知这个世界。
面向对象编程的核心思想
编程就是对现实世界的抽象,借助于上述哲学思想,面向对象编程定义了两个最核心的名词——类(class)和对象(object)。类实际上就是我们用来定义某个概念的,例如我们定义“Car”这么一个类,那么就在我们的程序世界里建立起了“Car”的概念。对象就是实体,例如我们用"Car"定义“myCar”这么一个对象,那么这个“myCar”就不再是一个概念了,而是一个独一无二的、具体的实体,“myCar”就是指我的那辆车,不再是别的车了。这个过程和柏拉图的理型世界有些相似,先有概念后有实体。
程序的组织方式就是分类,例如“汽车”可以分为“自动档汽车”和“手动档汽车”,那么我们继承"Car",定义 “AutoCar” 和 “ManuCar” 两个类。这里提到了继承这个概念,待会还要详说。
那么,通过什么来描述一个概念(类)呢?换而言之,如何定义一个类呢?
首先,任何事物都会有它的属性。例如一个人,他(她)有区别与动物的社会属性、劳动属性等等,另外还会有身高、体重、年龄、性别、户籍、民族等等属性。属性描述了事物的静态特性,除此之外还需要描述事物的行为特性,这就是函数。函数(function)有功能的意思,它意味着类能完成一些功能。例如人能走路、说话、跳舞,这些就是人的行为特性,我们用函数来实现。从外界来看,人能完成走路、说话、跳舞等功能,假设这个人是机器人,那么外界就可以请求该机器人完成这些功能。简而言之,我们通过属性(C++中叫变量)和函数定义类。
回到之前的汽车问题:假设有一辆汽车,简化成一个质点,从A点直线运动到B点,AB的距离为500m,汽车要经历启动、加速、匀速、刹车等过程,汽车有最大速度,当加速到最大速度后匀速运动,刹车距离是20m,请用面向对象的方法来描述这个问题。
这个问题中仅涉及到一个概念,即“汽车”,所以定义一个"Car"的类,描述“Car“的属性主要有m_currVilocity(当前速度),m_maxVilocity(最大速度),m_currDistance(当前行驶距离);描述“Car“的功能行为主要有start(),accelerate(),uniformMotion(),stop()等。根据这些,我们定义"Car"这个类:
class Car{
private:
float m_currVilocity, m_maxVilocity, m_currDistance;
public:
void start();
void accelerate();
void uniformMotion();
void stop();
float getCurrVilocity();
float getMaxVilocity();
float getCurrDistance();
}
再通过"Car"这个类定义一个对象"myCar"(实体),就可以对这个对象进行操作了。
void main(){
Car myCar;
myCar.start();
while(myCar.getCurrVilocity()<myCar.getMaxVilocity()) {
myCar.accelerate();
}
while(500- myCar.getCurrDistance()<20){
myCar.uniformMotion();
}
myCar.stop();
}
到此,我们已经完成了简单的面向对象的编程,这时候初学者一定会纳闷,这样搞比面向过程的方法麻烦多了,有什么好的。这个问题暂时搁置一下,先看看面向对象的三个基本特性。
面向对象的三个基本特性
面向对象的三个基本特性分别是封装性、继承性、多态性。
(1)封装性。比较细心的同志会发现我在定义属性的时候使用了"private"这个关键词,这表示这些属性不能被外界直接访问和修改,这就是封装性。之所以这么做,是为了隐藏复杂性,包括两方面的原因:一是对客户隐藏他们不需要知道的细节,让客户专注到如何使用接口上来,这也可以防止他们窥探类的内部设计思想;二是允许库设计人员修改内部结构,不用担心它会对客户程序员造成什么影响,只要对外接口不变,类的内部变化不会对外界造成任何影响。所以封装让类保持了一定的独立性,有利于设计高内聚、低耦合的程序。
(2)继承性。上面提到了 “汽车”可以分为“自动档汽车”和“手动档汽车”,所以能继承"Car",定义 “AutoCar” 和 “ManuCar” 两个类,这就是继承性。 这里,"Car"被称为父类,而 “AutoCar” 和 “ManuCar” 称为子类,子类会继承父类的所有属性和函数,所以 “AutoCar” 和 “ManuCar” 就复用了"Car"的所有属性和功能。但继承不止有类的继承的这种方式,在JAVA里面还有接口的继承,这个留给“JAVA与C++的区别”这篇文章。另外,继承性也不仅仅只体现在继承上,还可以通过“组合”来实现,而且有时候使用组合会带来更好的程序结构,这个也留到以后再写。
(3)多态性。“AutoCar” 和 “ManuCar” 两个类纯粹继承"Car"还不够,否则就和"Car"一模一样了。我们知道,自动档汽车和手动档汽车主要区别在于加速的方式不一样,所以就要重写accelerate()这个函数,通过重写,“AutoCar”定义的对象和 “ManuCar”定义的对象再加速时就会呈现不同的结果,这就是多态性。程序的多态性还包括“函数重载”,在C++里面,允许存在多个同名函数,但是参数表不同,或者参数类型不同,或者两者都不同,这就是“函数重载”。
了解了三个基本特性后,再回到之前那个问题,很多新学面向对象语言的人都会觉得面向对象要比面向过程麻烦多了,我当初也是这种感觉。但是当问题复杂一些时:汽车厂升级了该款汽车,加速度和最大速度都要都比改进的汽车大,厂家想通过一次比赛试验汽车的改进效果,于是改进前的汽车和改进后的汽车从A点同时出发,看哪一辆先到达B点。
用面向对象的方法轻而易举就完成了,我通过Car 定义两个对象就行了,一个是改进前的car1,一个是改进后的car2,这两个Car只是属性有所不同而已,然后就可以对这两个Car进行操作了。
更复杂的情况:汽车增加新功能,可以转弯壁障,另外路上不只有这两辆车,还有公交车、货车、自行车,路面也不这么简单了,分成了私家车车道、公交车车道、非机动车车道和人行道,还有红绿灯、指示牌等交通管理设备。
公交车、货车、自行车等都是交通工具,定义一个交通工具的类,找到他们的共有属性和行为,在交通工具这个类中定义,然后再继承交通工具的类,定义公交车、货车、自行车,找到他们的不同点,添加各自独特的属性和函数,改写与父类有区别的。然后再定义公路、交通管理设备等类,完成这些类的定以后,再根据实际情况定义不同的对象,对象之间进行交互,就能把这个复杂的问题给解决了,这就是封装性、继承性、多态性带来的神奇力量!
本文主要介绍了面向对象编程的核心思想和三个基本特性,但要入门面向对象编程知道这些还远远不够,还有许多需要学习和掌握的,且听下回分解。
网友评论