序言
计算机革命起源于机器,因此编程语言的产生也开始于对机器的模仿。但是计算机并非只是机器那么简单。其俨然已成为一门科学,就算有人倾其一辈子也难参透其中的所有奥秘,但就如同我们的大脑,能用其不断延伸,如写作、绘画、雕刻、动画等表达形式、面向对象程序设计(OOP)便是这种以计算机作为表达媒体的大趋势中的组成部分。
1.1 抽象过程
所有的编程语言都提供抽象机制。可以认为,人们所能解决问题的复杂性直接取决于抽象的类型和质量。
其实各种的编程语言也是一种轻微的抽象,如汇编语言就是对底层机器的轻微抽象。面向对象的编程方式,使程序员不再受限于任何特定类型的问题,程序可以通过添加新的类型来解决特定的问题,这使得我们解决问题时更加灵活。所以,OOP允许根据问题来描述问题,而不是根据计算机结构来解决问题。
以下是Alan kay曾经总结了第一个成功的面向对象语言、同时也是Java所基于的语言之一的Smaltalk的五个基本特性,这些特性表现了一种纯粹的面向对象程序设计方式:
1)万物皆对象。将对象视为奇特的变量,它可以存储数据,除此之外,你还可以要求它在自身上执行操作。
2)程序是对象的集合,他们通过发送消息来告知彼此的交互。发送消息指的是对象方法的调用。
3)每个对象都有其类型。类型我们用class,来表达,也就是我们平常所说的类,这使得什么样的对象拥有了什么样的方法(消息)。
4)每个对象都有自己的由其他对象所构成的存储。可以在新对象中创建现有对象,因此,可以再程序复杂的结构体系中,将复杂性一个一个地隐藏在对象的简单性背后。
5)某一特定类型的所有对象都可以接收同样的消息。举个例子,“黑人”类型的对象同时也是“人类”类型的对象,因此可以编写与“人类”类型交互并且自动处理与人类性质相关的事物的代码。
总的来说就如Booch对对象的简洁描述:对象具有状态、行为和标识。每一个对象在内存中都有一个唯一的地址。
1.2 每个对象都有一个接口
创建抽象数据类型是面向对象设计的基本概念之一。我们在面向对象设计中其实是新建的数据类型,事实上大部分的设计都是用class关键字来代表,但实际上对象数据类型和内置的数据类型基本一致,这使得程序员能够更加灵活地适应问题,来解决他们,而不再局限于内置的类型,这使得程序有了很强的扩展性。因此一旦类型建立了,那么就可以随心所欲地创建相应的类型对象。
但是在获得有用的对象时,就得知道其接口所定义的方法,哪些是我们所需要的,如:
A a=new A();
a.write();
接口确定了相应类型对象的请求方法。但是在程序中必须有相应的实现。
1.3 每个对象都提供服务
在实际的开发中,我们尽量把对象想象成“服务的提供者”,这不仅仅使得我们有了很好地将难问题一步步抽象成小问题的解决办法,而且这能提高我们程序的内聚性。如Java中对于数据库的操作有时我们会将数据库的连接、操作、关闭放在一个对象中操作,这虽然看起来很流畅,但是其中的操作过多,特别是对数据库的操作方法,因此,以“服务的提供者”为宗旨,我们可以将数据库的连接和关闭作为一个对象,数据库的操作为一个对象,这使得对象不去做过多的事情,同时提高了代码的复用。
1.4 被隐藏的具体实现
将程序开发人员按照角色分类为类的创建者和客户端程序员是大有裨益的。类的创建者负责类的具体实现,其权限可以进行类的任何操作,客户端程序员则调用类的暴露部分,对类的内部实现不必关心,也没有权限访问。这种吗模式使得只关心客户端的程序员在开发时不会修改到对象内部的脆弱隐藏部分,减少了开发中出现的bug。
基于这种思想,Java有了自己的内部设定边界:
1.public 表示公开的,对任何人都是开发的,可用的。
2.private 表示私有的,除了类的创建者和类型的内部方法之外任何人都不能访问的元素。
3.protect和private类似,但是继承的类可以访问非private的成员
4.当没有加任何任何边界时,其有一种默认的访问权限,其被称为包访问权限,包外的成员就和private访问权限类似。
1.5 复用的具体实现
代码复用是面向对象程序设计语言最了不起的有点之一。在创建一个新类时,可以使用现有任何对象来实现其新的功能,这种方式叫做组合,如果组合是动态的就叫做聚合,就想“汽车拥有引擎”一样,由于在面向对象程序设计中继承被高度强调,因此有时候会出现很复杂的设计,但实际上在穿件新的类时,应该首先考虑组合关系,一旦有了一些经验之后,便能够看出什么时候必须使用继承了。
1.6 继承
在实际开发中我们经常会遇到这样的情况,有很多现有的功能在一个类中,但是我想创建另外一个类拥有与其相似的功能,这让我们想到了继承,一个类作为基类,其子类可以有相同的特性和行为,但是子类可能比基类拥有更多的特性。同时子类他复制了基类的接口,也就是所,当发给基类的消息,那么子类也能收到,由于通过发送给类的消息的类型可知类的类型,所以这也意味着子类和基类具有相同的类型。
在继承中只是产生了让子类对象拥有了基类的相同行为和类型,那么这完全是有没有意义的。因此产生差异就有了两种方法。
1.子类完全可以有更多的特性,也就是更多的方法,这些方法并不是基类接口的一部分,这意味着基类已经不能满足你的需求,因此需要添加更多。
2.子类可以进行对基类方法的覆盖(overriding),也就是说使用相同接口的方法,但是在子类的方法做些不同的事情。
1.7 伴随多态的可互换对象
在处理类型的层次结构时,经常想把一个对象不当做它所属的特定类型来对待,而是将其当做其基类的对象来处理。
这种泛化操作时非常方便的,但是却给编译器带来了不少的麻烦,因此,在后来的OOP中,程序知道运行时才能够确定代码的地址,也就是后来所说的后期绑定概念。当对象发送消息时,被调用的代码知道运行时才能确定,并且在JAVA中这种方式使默认行为,不需要像C++那样加上额外的关键字。请看下面的代码:
void doSomething(Shape shape){
shape.erase();
shape.draw();
}
Circle circle =new Circle();
Triangle triangle=new Triangle();
doSomething(circle);
doSomething(triangle);
在Java中,doSomething()的调用会自动正确处理,而不管对象的确切类型。这种将子类看做是它的基类的过程称为向上转型,这也是多态的神奇之处。
接下来的内容会在下一篇博文中继续分享,希望大家能多多提意见交流