大一下学期,我第一次接触到高级语言程序设计——C语言。完成了几次作业以后,我学会了定义全局变量,就是把一些经常使用的参数定义在所有函数体外,以免在编程过程中频繁地在函数体之间传递指针,从而使得程序设计变得“容易”了(只能说编程,还谈不上设计)。这就是我们习惯称呼的面向过程的程序设计(Procedure-Oriented Programming,POP)。,不久后,我们几个同学自学JAVA组织参加冯如杯科技竞赛。在我们的程序里,受C语言的影响,把类的属性声明为“public”成了家常便饭。拿着面向对象的语言去写面向过程的程序,确实让我们吃了不少苦头。
面向过程的程序设计,是结构化的软件构建技术,像前面所说的一样,它习惯把数据、函数体和函数库暴露给算法,使得数据和函数体绞在了一起,修改函数体而不影响程序代码几乎不可能。由于数据和结构对说有函数都可见,你根本把握不住数据的变动是由哪一个函数引起的,一旦程序规模变大,当很多程序员共同开发一个项目时,你会发现,不同的函数对同一个数据的访问,将会导致一场噩梦!
60年代后期,Simula 67 语言开始引入面向对象程序设计(Object-Oriented Programming,OOP)的概念。
90年代始,面向对象技术就像是一股飓风,突然间让几乎所有的程序员开始对它忠心耿耿,JAVA、C++这些语言,都成了大家的最爱。
是什么力量使得面向对象技术这么让人倾心?想知道答案吗?请跟随我一起走进OOP。
在现实生活中,各个事物都有它自己的行为(Performance),而且事物与事物之间有着千丝万缕的联系。就像我们和发电厂的关系一样,我们使用电脑需要电,但是我们并不自己发电,我们只需要把插头往插座上一插,电流便如同泉水般流来。在这里,电脑和发电厂就是两个独立的对象(Object),插座就扮演了OOP中的接口(Interface)的角色。电脑用电,它并不需要去关心发电厂是怎么给它提供的,或者说,这么多发电厂,究竟用的是那家的电,对电脑来说,知道与不知道,无关紧要,它只关心,插上插头,要能都到电流!这样,发电厂在电脑面前就把自己的行为和属性(Attribute)封装起来了。(附带解释一下属性:发电厂的名字、规模、员工等等,这些就是它的属性,发电厂要正常的工作,必须要有一些属性的参与。同样电脑也有自己的属性,例如,屏幕大小、CPU品牌、主板型号之类。)一个对象要给另一个对象提供服务,它只需要保留供给对方使用的接口,服务具体是怎么实现的,提供服务的对象就把这个细节悄悄地隐藏起来。我们把对象的属性和行为隐藏起来的过程就叫做封装(encapsulation)。让我们来看一段JAVA程序:
public class PowerStation ... {
protected int power; // 功率
public PowerStation(int power)...{
this.power = power;
}
public int getPower()...{
return power;
}
}
/**/ /* 电脑对象,它有一个固有功率属性。它的行为是将自己得到的功率数输出到屏幕上。*/
public class Computer ... {
private int myPower;
public Computer()...{
myPower = 15; // 固有功率是15
}
public void print()...{
PowerStation ps = new PowerStation(myPower); // 生成发电厂对象
// 使用发电厂的getPower() 接口
System.out.println("我是电脑,我的的用电瓦数是:"+ ps.getPower());
}
}
/**/ /* 在main 函数中测试。*/
public class Test ... {
public static void main(String args[])...{
Computer PC = new Computer();
PC.print();
}
}
测试的结果如下:
我是电脑,我的的用电瓦数是:15;
这样,我们就模拟了电脑与发电厂的交互过程。是不是很有趣啊?
从现在起,我们就应该意识到,如果发电厂工作过程有所改变,则我们只需要在函数体内部进行相应的代码改动即可,并不需要去改变Computer类(对象)的函数调用。因为,使用服务方的行为对与提供服务者来说,也是封装好了的。打个比方,发电厂以前是用水力发电的,而现在改用核能发电了,而这些东西不需要也不应该让用电者知道,发电厂需要做的工作只是保证用户把插头插上后,能够得到电流。
让我们想想面向过程的程序设计中代码改动带来的后果,OOP与之比起来是不是更容易维护了啊?再也不用“动之一毛则动全身”了,不得不说OOP是程序设计发展之路的一座里程碑!
大家有了兴趣,那么就再稍微深入一点来看看面向对象技术。
发电厂有供电的行为,但是发电方式却是各不相同的。有水力发电、火力发电、风力发电,甚至是核能发电。虽然怎么发电对于用户无关紧要,但是对于发电厂内部,我们则要正确的处理各个不同的发电过程。在此,我们引入了继承(Inherit)的概念.
发电厂是父亲,水力发电长则是它的儿子,因为儿子具备父亲的遗传特性,拥有power属性和提供一定功率的电流的行为,而且还在之上拓展了发电方式,所以我们说水力发电厂“继承”了发电厂,以此类及火力发电厂等等。看看下面的程序:
public class WaterPowerStation extends PowerStation ... {
public WaterPowerStation(int power)...{
super(power); // 因为是继承,所以借用父类的函数给power赋值
}
public int getPower()...{
System.out.println("这是水力发电厂!");
return power;
}
}
/**/ /* Computer 类中print() 方法做点修改。*/
public void print() ... {
// 生成“水力发电厂”对象
WaterPowerStation ps = new WaterPowerStation(myPower);
// 使用水力发电厂的getPower() 接口
System.out.println("我是电脑,我的的用电瓦数是:"+ ps.getPower());
}
在main 函数中测试的结果如下:
这是水力发电厂!
我是电脑,我的的用电瓦数是:15
看,对于发电厂内部来说,我们把工作交给了水力发电厂来完成,然而,不管发电情况怎样,电脑还是一如既往地得到了15瓦的电流,它并没有因为发电方式改变了,从而跟着去改变它得到电流的方式,所以前后都是用getPower() 来获得服务,这就是面向对象的优势!当然,举一反三,我们还能写出火力发电厂,核能发电厂等等的子类。
好了,发电厂嫌我老是提起它,开始有点烦了,我也不想跟它结仇,毕竟以后需要它的时候还多着呢。我们换一个的例子吧。到动物的世界去了解面向对象技术中的多态性(variety)。看下面的例子:
abstract public class Animal ... {
// 定义一个吠叫的行为,所有动物继承了这个类,就要去实现这个行为。
// 现实生活中的表现是:只要是动物,就能吠叫。
abstract public void barking();
}
众所周知,猫和狗都是动物,虽然他们都是继承动物类,都具有动物的“吠叫”行为,但是他们却不完全相同,表现之一就是叫声不同。我们现在来构造一个猫类(请允许我没有用“一只”来修饰猫):
public class Cat extends Animal ... {
// 实现barking()抽象方法——猫叫
public void barking()...{
System.out.println("我是猫,喵喵叫");
}
}
/**/ /* 狗类 */
public class Dog extends Animal ... {
// 实现barking()抽象方法——狗叫
public void barking()...{
System.out.println("我是狗,汪汪叫");
}
}
/**/ /* 在main 函数中测试。*/
public class Main ... {
public static void main(String args[])...{
Animal an= new Cat(); // 生成一个动物
an.barking();
dog = new Dog();
an.barking();
}
}
在main 函数中测试的结果如下:
我是猫,喵喵叫
我是狗,汪汪叫
注意定义时,我们只是声明了一个动物Animal an,具体是什么动物,我们并不在意(可以与讲继承时候的WaterPowerStation ps = new WaterPowerStation(myPower) 作作比较,在那儿,我们没有用PowerStation ps,我们是声明的一个子类。而这儿,我们使用父类来声明的。),然而,我却可以用它来实现猫和狗不同的叫声。那么,这样做有什么意义呢?
其实,这一切都是为了模仿现实,这样才能让我们更容易的用实际生活中的经验去进行软件开发,这也是面向对象的着眼点——万物皆对象。
设想我们经常谈论鸟,我们说鸟叫,但是鸟的种类千差万别,叫声也各个不同,难道我们在给别人谈鸟叫的时候,非得把鸟类分为黄莺、喜鹊……再一一得作以讲述吗?即使是,那么黄莺里边还有很多种类,喜鹊也是一样。在这个时候,我们就用鸟类来替代了它们的全部,现实生活证明了这种替代方法其实工作的很好!其中乃以支撑这种方法的原因,正是多态性。
我们在描述一个父类的时候,声明一个抽象的接口(行为),让所有继承它的多种子类单独去实现这个方法,这样,同一个行为在不同子类中就实现了不同的功能,可以说,一个方法具有了多种形态。面向对象的设计中,我把所有子类统一用其父类对象来表示,然后调用统一的接口,那么实现它的子类就会自动的调用具体化的方法,继而就有了“鸟叫”。这种同一个行为在不同子类中就实现了不同的功能的性质就叫多态性。
以上就是面向对象技术OOP的一些概要,相信它的很多性质会让你产生浓厚的兴趣,要是这样,那还不赶紧加入OOP的队伍,现在不会觉得这么多人追逐OOP的现象很奇怪了吧!