对象导论

来源:互联网 发布:韩国snow软件 编辑:程序博客网 时间:2024/05/01 04:14

1.1 抽象过程

所有语言都提供抽象机制,可以认为,人们能够解决的问题的复杂性直接取决于抽象的类型和质量。 所谓类型是指所抽象的是什么。 

  • 汇编语言是对底层机器的轻微抽象。
  • FORYRAN、BASIC、C等都是对汇编语言的抽象。

这些语言虽在汇编语言上有了大幅的改进,但它们所做的抽象仍要求在解决问题时机遇计算机的结构,而不是基于所要解决的问题的结构来考虑。

程序员必须建立起在机器模型和实际待解决问题的模型之间的关联。

机器模型:位于解空间内,这是对问题建模的地方,例如计算机。

实际待解决问题的模型:位于问题空间,这是问题存在的地方,例如一项业务。

面向对象方式将问题空间中的元素及其在解空间的表示称为"对象"。实质是:程序可以通过添加新类型的对象使自身适用于某个特定的问题。因此,当阅读描述解决方案的代码的同时,也是在阅读问题的描述。

如果与现实世界中的对象作类比,它们都具有特性和行为。

面向对象程序设计方式的特性:

  • 万物皆对象。将程序视为奇特的变量,它可以存储数据,除此之外,你还可以要求它在自身上执行操作。理论上讲没,你可以抽取待求解问题的任何概念化构件。

  • 程序是对象的集合,它们通过发送消息来告知彼此所要做的。 消息可以想象为对某个特定对象的方法的调用请求。

  • 每个对象都有自己的由其他对象所构成的存储。 通过创建包含现有对象的方式创建新类型的对象。

  • 每个对象都拥有其类型。 每个对象都是某个类的一个实例。

  • 某一特定类型的所有对象都可以接收同样的消息。 圆形类型的对象同时也是几何形类型的对象,所以圆形对象必定能够接收发送给几何形对象的消息。这种可替代性是OOP中最强有力的概念之一。

更简洁的描述,对象具有状态、行为和标识:

  • 状态:每个对象都可以拥有内部数据。
  • 行为:每个对象都可以拥有方法。
  • 标识:每个对象在内存中都有一个唯一的地址。

1.2 每个对象都有一个接口

创建抽象数据类型(类)是面向对象程序设计的基本概念之一。

抽象数据类型的运行方式与内置类型完全一致:你可以创建某一类型的变量(对象或实例),然后操作这些变量(发送消息)。每个类的成员或元素都具有某种共性。

由于类描述了具有相同特性(数据元素)和行为(方法)的对象的集合,所以一个类实际上就是一个数据类型。 现在,程序员可以通过定义类来适应问题,而不再被迫使用机器中的存储单元的数据类型。 并且,可以根据需求,通过添加新的数据类型来扩展编程语言。

面向对象程序设计的挑战之一就是:问题空间的元素和解空间的对象之间创建一对一的映射。

每个对象都只能满足某些请求,这些请求由对象的接口(interface)所定义,决定接口的便是类型。接口确定了对某一特定对象所能发出的请求。

1.3 每个对象都提供服务

当正在试图开发或理解一个程序设计时,最好的方法之一就是:将对象想象成服务提供者。 程序本身将向用户提供服务,它通过调用其他对象提供的服务来实现这一目的。我们的作用便是:创建(或从现有代码库中寻找)能够提供理想的服务来解决问题的一系列对象。

我们可以在程序设计时,问自己,如果将问题从表象中抽取出来,那么什么样的对象可以马上解决该问题。 有可能有些对象已经存在,但对于并不存在的对象,它们应该是什么样子?它们能够提供哪些服务?它们需要哪些对象协助才能提供这些服务?这是将问题分解为对象集合的一种合理方式。

将对象看做是服务提供者还有一个好处:有助于提高对象的内聚性。 高内聚是软件设计的基本质量要求之一:这意味着一个软件架构的各个方面组合得很好。 我们在设计对象时所面临的一个问题是,将过多的功能塞在一个对象中。在良好的面向对象设计中,每个对象都可以很好地完成一项任务,并不试图做更多的事。

将对象作为服务提供者看待:不仅在设计过程非常有用,而且让其他人试图理解你的代码或重用某个对象,变得更加简单。

1.4 被隐藏的具体实现

我们将程序开发人员按照角色分为:

  • 类创建者:创建新数据类型的程序员。目标是:构建类,只向客户端程序员暴露必需的部分,通过访问控制隐藏其他部分。

  • 客户端程序员:在其应用中使用数据类型的类消费者。目标是:收集各种用来实现快速应用开发的类。

访问控制的存在原因:

  1. 使客户端程序员无法触及他们不应该触及的部分。(这部分对数据类型的内部操作来说是必须的,但并不是用户解决特定问题所需接口的一部分。)

  2. 允许类库设计者可以改变类内部的工作方式而不必担心会影响客户端程序员。 如:当你为了提高效率,必须改写某个类的实现时。并且,我们发现如果接口和实现可以清晰地分离并得以保护,我们可以轻易地完成这个需求。

Java用三个关键字在类的内部设定边界(决定了紧跟其后被定义的元素的访问限制):

  • public:任何人都可用。

  • private:除类型创建者和类型的内部方法外的任何人都无法访问。

  • protected:在private的基础上,继承的类也可以访问。

Java还有一个默认的访问权限,即不使用任何访问指定词。这种权限通常被称为包访问权限:类可以访问同一个包中的其他类的成员,但在包之外,这些成员如果被限定为private。

1.5 复用具体实现

代码复用是面向程序设计语言所提供的最了不起的优点之一,产生一个可复用的对象设计需要丰富的经验和敏锐的洞察力。

最简单地复用某个类的方式就是直接使用该类的一个对象,将该对象作为成员置于新类中。 即使用现有的类合成新的类。这种概念被称为:组合。 如果组合是动态发生,则称为聚合。 组合经常被视为"has-a"(拥有)关系。就像汽车拥有引擎一样。

在组合中,我们通常将新类的成员对象声明为private,使用新类的客户端程序员不能访问它们。这样,我们便可以不干扰现有客户端代码的情况下,修改这些成员。并且通过在运行时修改这些成员对象,可以实现动态修改程序的行为,从而提高组合的灵活性。 继承则不具备这样的灵活性,因为编译器必须对通过继承而创建的类施加编译时的限制。

1.6 继承

对象将数据和功能封装到一起,从而可以对问题空间的观念给出恰当的表示,而不用受制于必须使用底层机器语言。 类型用关键字class表示,它们形成了面向对象编程语言中的基本单位。

我们会发现,在创建一个类之后,即使另一个新类与其具有相似的功能,仍需重新创建一个新类。所以,继承便由此而来,继承的出现让我们能够以现有的类为基础,复制它,然后通过添加和修改这个副本来创建新类。 不过,缺点是当基类发生变动时,导出类也会发生相应的变动。

继承使用基类型和导出类型表示了一种类型与类型之间的相似性的概念:基类型包含其导出类型所共享的特性和行为。

基类型表示系统中某些对象的核心概念,而从基类型中导出的其他类型,则表示此核心可以被实现的各种不同方式。

如:基类是几何形,每一个几何形都具有尺寸、颜色、位置等,同时每一个几何形都可以被绘制、擦除、移动和着色等。在此基础上导出的圆形、正方形、三角形等,每一个都具有额外的特性和行为。

当继承现有类型的同时,也创造了新的类型。这个新类型,不仅包括现有类型的所有成员,而且复制了基类的接口。所以,所有发送给基类对象的消息同时也可以发送给导出类的对象,即导出类与基类具有相同的类型。

由于基类和导出类具有相同的基础接口,所以伴随此接口的必定有某些具体实现。也就是说,当对象接收到特定消息时,必须有某些代码去执行。如果只是简单地继承一个类而不做其他改变,那么基类实现接口中的方法将会直接继承到导出类中。这意味着,导出类不仅与基类拥有相同的类型,而且还拥有相同的行为。这种做法毫无意义。

有两种方法可以使基类与导出类产生差异:

  • 直接在导出类中添加新方法。这些方法并不是基类接口的一部分。这意味着基类不能直接满足需求,因此必须添加更多的方法。这种方式有时是一种完美的解决方式,但在使用这种方式时,应该仔细思考是基类是否也需要这些额外的方法。

  • 改变现有基类的方法的实现,被称为:覆盖。如果要覆盖某个方法,只需在导出类中创建该方法的新定义即可。覆盖的意义是:使用相同的接口方法,但想在心类型中做些不同的事情。

1.6.1 "是一个"与"像是一个"关系

继承可能产生的导出类和基类的关系:

  • is-a:导出类只覆盖基类的方法,而不添加基类中不存在的方法。 这样的话,导出类和基类具有完全相同的类型,可以用一个导出类对象来完全替代一个基类对象,这通常被称之可替代原则。 如:一个圆形就是一个几何形。

  • is-like-a:在导出类型中添加新的接口元素,即扩展接口。 这样的话,由于基类无法访问新添加的方法,使用导出类替代基类则不完美。这种情况,我们可以描述为is-like-a的关系。新类型具有旧类型的接口,但它还扩展了一个新的接口,所以它们并非完全相同。

1.7 伴随多态的可互换对象

为了编写出不依赖于特定类型的代码,在处理类型的层次结构时,我们经常不关心一个对象的具体类型,而将其当作基类的对象来对待。

例如:在几何形的例子中,我们不关心是圆形、正方形、三角形还是其他尚未定义的形状,方法的操作都是泛化的形状。所有的几何形状都可以被绘制、擦除和移动,所以这些方法都是直接对一个几何形对象发送消息,它们不用担心对象将如何处理消息。

在面向对象程序中,我们经常为了处理新情况而添加新类型,最重要的是,上面的处理方式则不会受到影响。 例如:我们需要从几何形中导出一个新的子类型:五角形,此时并不需要修改处理泛化几何形状的方法。

不过,在试图将导出类型的对象当作其泛化基类型对象来看待时,编译器在编译时无法得知应该执行哪一段代码。

一个非面向对象编程的编译器产生的函数调用会引起所谓的前期绑定:编译器将产生一个具体函数名字的调用,而运行时,将这个调用解析到将要被执行的代码的绝对地址。

在面向对象程序中,程序直到运行时才能够确定代码的地址,所以当消息发送到一个泛化对象时,该对象无法得知对这条消息应该做些什么。

为了解决上述问题,面向对象程序设计语言使用了后期绑定:编译器确保被调用方法的存在,并对调用参数和返回值执行类型检查,被调用的代码直到运行时才确定。 为了达到这个目的,Java使用在对象中存储的信息来计算方法体的地址,并以此来替代绝对地址调用。 因此,每一个对象都可以具有不同的行为表现。

下面我们通过Java来描述几何形的例子,它忽略类型的具体细节:

    void doSomething(Shape shape) {        shape.erase();         //  ...        shape.draw();    }

这个方法独立于任何它要绘制和擦除的对象的具体类型,它可以工作于任何Shape:

        Circle circle = new Circle();        Triangle triangle  = new Triangle();        Line line = new Line();        doSomething(circle);        doSomething(triangle);        doSomething(line);        

在将Circle、Triangle、Line当作Shape传递给doSomething的过程中,发生了向上转型(upcasting)。 在继承图的典型布局方式中:基类在顶部,导出类在其下部散开。导出类转型为基类,就是在继承图中向上移动,即"向上转型"。

多态机制使得,当向一个对象发送消息时,即使涉及向上转型,该对象也知道要如何执行正确行为。

1.8 单根继承结构

在Java中,所有的类最终都继承自单一的基类Object,即Java属于单根继承结构。

在单根继承结构中,所有对象都具有一个公共接口,即,归根到底,它们都是相同的基本类型。所以,单根继承结构保证所有对象都具有某些功能,因此,系统可以在每个对象上都执行某些基本操作。

所有对象都可以很容易地在堆上创建,垃圾回收器的实现也变得容易。所有的对象都保证具有其类型信息,对于系统级操作(如异常处理)显得尤为重要。

1.9 容器

通常来说,如果不知道在解决某个特定问题时需要多少个对象,或它们将存活多久,就无法得知如何存储这些对象。

在面向对象设计中,解决方案为:创建另一种对象类型,这种新的对象类型持有其他的对象的引用。 这个被称为容器的新对象,在任何需要时都可以扩充自己以容纳将置于其中的所有东西。 因此不需要知道对象的具体数量,只需创建一个容器对象,然后让它处理所有细节。

幸运的是,好的OOP语言都有一组容器,它们作为开发包的一部分。Java在其标准类库中包含了大量的容器。在Java中,具有满足不同需求的各种类型的容器,例如:List(用于存储序列),Map(被称为关联数组,用来建立对象之间的关联),Set(每种对象类型只持有一个),以及诸如队列、树、堆栈等更多的构建。

Java设计如此多种类型的容器的原因有两点:

  • 不同容器提供了不同类型的接口和外部行为。 堆栈相比队列就具备不同的接口和行为,也不同于集合和列表的接口和行为。

  • 不同的容器对于某些操作具有不同的效率。 最好的例子是两种List的比较。在ArrayList中:随机访问元素是一个花费固定时间的操作,而在序列中间插入元素,花费确实高昂的。 在LinkedList中:随机访问元素需要在列表中移动,访问越靠近表尾,开销越大,而插入元素,则开销较小。 我们可以根据系统需求而选择不同效率的容器,接口的抽象降低了容器之间转换时对代码产生的影响。

1.9.1 参数化类型

在Java SE5之前,容器存储的对象都只具有Java中的通用类型:Object。 由于容器只存储Object,当对象引用置于容器时,它必须向上转型为Object,从而会丢失其具体类型。当把它从容器中取回时,只能获得一个对Object对象的引用,如果我们要获取具体类型,就需要向下转型:从泛化类型转型为具体类型。

向上转型是安全的,如我们知道Circle是一种Shape类型。 但我们无法得知某个Object是Circle还是Triangle,所以除非确切知道索要处理的对象的类型,否则向下转型几乎是不安全的。

不过,当我们向下转型为错误类型时,就会得到运行时异常,从而在运行时捕获错误。 尽管如此,当从容器中取出对象引用时,还是必须要以某种方式记住这些对象的具体类型,从而正确地向下转型。

向下转型和运行时的检查需要额外的程序运行时间,也需要程序员付出更多的精力。那么创建一个知道自己所保存对象的类型的容器是有意义的。这种解决方案被称为参数化类型机制。 参数化类型机制使得:编译器可以自动定制作用于特定类型上。 例如,通过使用参数化类型,编译器可以定制一个只接纳和取出Shape对象的容器。

Java SE5 的重大变化之一就是增加了参数化类型,在Java中它称为泛型。 一对尖括号,中间包含类型信息:

ArrayList<Shape> shapes = nwq ArrayList<Shape>();

泛型可以帮助我们写出更加泛化的代码,具有更好的复用性,很多标准类库构件都已经添加了泛型。

1.10 对象的创建和生命周期

在使用对象时,最关键的问题之一便是它们的生成和销毁方式。

每个对象为了生存都需要资源,尤其是内存。当我们不在需要一个对象时,它必须被清理掉,是占用的资源可以被释放和重用。

面向对象程序设计中,对象的创建方式有两种:

  • C++采用在栈或静态存储区域上创建对象:将存储空间分配和释放置于优先考虑的位置,在编写程序时确定对象的确切数量、生命周期和类型。 在栈中创建存储空间和释放存储空间通常各需要一条汇编指令即可,分别对应将栈顶指针向下移动和将栈顶指针向上移动。

  • Java采用在堆的内存池中动态地创建对象:存储空间是在运行时被动态管理,需要大量的时间在堆中分配存储空间,远大于在栈中创建存储空间的时间。 创建堆存储空间的时间依赖于存储机制的设计。

我们所关心的还有对象的生命周期:

  • 对于在栈上创建对象的语言,如C++,必须通过编程方式来确定何时销毁对象,这可能会因为不能正确处理而导致内存泄露。

  • 在堆上创建对象,如Java,提供了垃圾回收机制,它可以自动地发现对象何时不再使用,并继而销毁它。垃圾回收机制减少了不必要的代码,提供了更高层的保障,可以避免内存泄露问题。

1.11 异常处理:处理错误

由于设计一个良好的错误处理机制非常困难,所以许多语言直接略去这个问题,将其交给程序库设计者处理。因此,这种处理机制的主要问题在于,过分依赖于程序员的自身警惕性,这种警惕性不是编程语言所强制的,如果程序员不够警惕,这些机制就很容易被忽视。

异常处理则是将错误处理直接置于编程语言中。异常是一种对象,它从出错点被抛出,并被专门设计用来处理特定类型错误的相应的异常处理器捕获。异常处理就像是与程序正常执行路径并行的、在错误发生时执行的另一条路径。异常无法被忽略,它保证一定会在某处得到处理。 异常提供了一种从错误状况进行可靠恢复的途径,所以,不需要退出程序,你就可以进行校正,并恢复程序的执行,这有助于编写出更健壮的系统。

Java一开始就内置了异常处理,而且强制必须使用。它是编译器唯一可接受的错误报告方式。如果没有正确处理异常的代码,就会得到一条编译期错误。这使得错误处理变得容易。

异常处理不是面向对象的特征,它在面向对象语言出现之前就已经存在。不过在面向对象语言中,将异常表示为一个对象。

1.12 并发编程

在计算机编程中有一个基本概念,就是在同一时刻处理多个任务的思想。许多程序设计问题都要求,程序能够停下正在做的工作,转而处理某个其他问题,然后再回到主进程。

最初,程序员们用所掌握的有关机器底层的知识来编写中断服务程序,通过硬件中断来触发主进程挂起。 缺点是:难度较大,并且无法移植。

在一个程序中,彼此独立运行的部分被称为线程,将一个进程切分成多个可独立运行的部分(任务),从而提高程序的响应能力的行为被称为并发。

并发程序有一个隐患,共享资源。当多个并行任务都要访问同一项资源,就会出现问题。为了解决资源分配问题,当一个任务访问共享资源时,该资源将被锁定,直至该任务完成后,资源将被释放,下一个任务才能获取该资源的使用权。

Java的并发是内置于语言中的,Java SE5已经增添了大量额外的库支持。

1.13 Java与Internet

Java促使计算机编程语言向前迈进革命性的一步最重要的原因是,它解决了在万维网上的程序设计问题。

1.13.1 Web是什么

要理解Web,首先我们需要理解客户/服务器系统。

客户端/服务器系统的核心思想是:系统具有一个中央信息存储池(central repository of information),用于存储某种数据,它通常存在于数据库中,可以根据需要将它分发给某些人员或机器集群。

  • 服务器:信息存储池、用于分发信息的软件以及信息与软件所驻留的机器或机群。

  • 客户机:通过机器上的软件与服务器进行通信,以获取信息、处理信息,然后将它们显示的机器。

在客户/服务器计算技术中,一台服务器需要响应多个客户机。 这就会出现一系列问题:

  • 客户/服务器系统通常会涉及数据库管理系统,并且系统会允许客户在服务器中插入新的信息,所以,我们需要保证:一个客户插入的新数据不会覆盖另一个客户插入的新数据,也不会在将其添加到数据库的过程中丢失(这被称为事务处理)。

  • 客户端软件发生变化,那么它必须被重新编译、调试并安装到客户端机器上。如果想支持多种不同类型的计算机和操作系统,问题将更加麻烦。

  • 最重要的是性能问题:可能在任意时刻都有成百上千的客户向服务器发出请求,所以任何小的延迟都会产生重大影响。 为了将延迟最小化,我们通常分散地给客户端机器处理,有时也会使用中间件将负载分散给服务器端的其他机器。(中间件也被用来提高可维护性)

Web实际上就是一个巨型客户/服务器系统:所有的服务器和客户机都同时共存于同一个网络中。

最初,Web只有一种简单的单向过程:客户机对某个服务器产生一个请求,然后服务器返回给客户机一个文件,客户机上的浏览器软件根据本地机器的根式来解析这个文件。 

经过发展,可以实现完整的客户/服务器能力,客户可以反馈信息给服务器。 例如:在服务器上进行数据库查找,将新信息添加到服务器以及下订单。

1.13.2 客户端编程

Web最初的服务器---浏览器设计是为了能够提供交互性的内容,但是其交互性完全由服务器提供。服务器产生静态页面,提供给只能解释并显示它们的客户端浏览器。

基本的HTML(HyperText Markup Language,超文本标记语言)包含有简单的数据收集机制:文本输入框、复选框、单选框、列表和下拉列表以及按钮。它只能被编程来实现复位表单上的数据或提交表单上的数据给服务器。 

这种提交动作通过所有的Web服务器都提供的通用网管接口(common gateway interface,CGI)传递。 通过CGI程序构建的网站可能会:过于复杂而难以维护,响应时间过长。CGI程序的响应时间依赖于所发送的数据量的大小,以及服务器和Internet的负载。

这种方式下,浏览器并不具备显著的交互性,只要是通过编程来实现的任务,就必须将信息发回到服务器去处理。 如:对Web输入表单进行数据验证的过程。 问题的解决方法就是客户端编程。 大多数运行Web浏览器的机器都是能够执行大型任务的强有力的引擎,客户端编程使它从原本闲置的状态工作起来,使返回给用户的结果更加迅捷,使得网站更具有交互性。

客户端编程的问题是:它与通常意义上的编程平台不同。Web浏览器就像一个功能受限的操作系统。下面的部分阐述了客户端编程的问题和方法:

  • 插件开发: 程序员可以下载一段代码,只需要一次,并将其插入到浏览器中适当的位置,以此来为浏览器添加新功能。 插件对于客户端编程的价值在于:它允许专家级的程序员不需警告浏览器生产厂商的许可,就可以开发某种语言扩展,并将它们添加到服务器中。 因此,插件使得可以创建新的客户端编程语言。

  • 脚本语言: 插件引发了浏览器脚本语言(scripting language)的开发。通过使用某种脚本语言,可以将客户端程序的源代码直接嵌入到HTML页面中,解释这种语言的插件在HTML页面被显示时自动激活。脚本语言只是作为HTML页面一部分的简单文本,当服务器收到要获取该页面的请求时,它们可以被快速加载。此方法的缺点是:代码会暴露给任何人去浏览(或窃取)。 JavaScript: 不需要任何插件就可以在Web浏览器上运行的脚本语言。它和Java没有任何关系,如此命名只是希望赶上Java潮流。JavaScript的错误处理的调试相当麻烦。

  • Java: 如果脚本语言无法解决客户端编程的问题,那么Java则是处理它们的最佳方案。Java不仅是一种功能强大的、安全的、跨平台的、国际化的编程语言,而且它还在不断地被扩展,以提供更多的语言功能和类库,能够优雅地处理在传统编程语言中很难解决的问题,例如并发、数据库访问、网络编程和分布式计算。 Java是通过applet以及Java Web Start来进行客户端编程的。 applet是只在Web浏览器中运行的小程序,它是作为网页的一部分而自动下载的(就像网页中的图片)。当applet被激活时,它便开始执行一个程序。 如果一台计算机有浏览器,并且浏览器具有内置的Java解释器,这个程序就可以自动在这台计算机上运行。客户端因此可以做许多事情,例如不必跨网络地发送一张请求表单给服务器验证,不仅获得了快速响应,而且降低了网络流量和服务器负载。 由于安装Java运行时环境(JRE)所必须10MB带宽,IE中并未包含JRE。Java applet始终没有得到大规模应用。

  • Flex: 允许创建基于Flash的应用。由于Flash Player在超过98%的Web浏览器上都可用,因此它被认为是已被接受的标准。安装和更新Flash Player都十分快捷。Flex使我们在编程时无需担心浏览器相关性。对客户端编程而言,这是一种值得考虑的备选方案。

  • .NET和C#: 曾几何时,Java applet的主要竞争对手是微软的ActiveX---尽管它要求客户端必须运行在Windows平台。从那以后,微软以.NET平台和C#编程语言的形式退出了与Java全面竞争的对手。.NET平台大致于Java虚拟机(JVM,即执行Java程序的软件平台)和Java类库,而C#毫无疑问与Java有类似之处。

  • Internet和Intranet: Web是最常用的解决客户/服务器问题的方案,当Web技术仅用于特定公司的信息网络时,它就被称为Intranet(企业内部网)。Intranet可以物理地控制对公司内部服务器的访问,比Internet提供了更高的安全性。 程序运行在Internet上时,无法得知它将运行的具体平台,因此,需要跨平台,安全的语言,如脚本语言和Java。程序运行在Intranet上时,可以对代码质量负责,并且发现bug后可以进行修复。

1.13.3 服务器端编程

服务器端编程是Java取得巨大成功的因素之一。 当提出对服务器请求后,服务器会作何响应:

大部分时候,只是简单地请求一个文件,之后浏览器会以某种适当的形式解释这个文件,例如将其作为HTML页面、图片、Java applet或脚本程序等来解释。

更复杂的对服务器的请求通常设计数据库事务:

  • 常见的形式是复杂的数据库搜索请求,然后服务器将结果进行格式编排,使其成为一个HTML页面发回给客户端。(如果客户端通过脚本程序具备了更多的智能,那么服务器可以将原始数据发回,然后在客户端进行格式编排,这样处理速度更快,服务器负载将更小。)

  • 另一种常见形式是对数据库的修改请求,这些数据库请求,必须通过服务器端某些代码来处理,这就是所谓的服务器端编程。基于Java的Web服务器,它使得我们可以用Java编写被称为Servlet的程序来实现服务器端编程。

1.14 总结

过程型语言:数据定义和函数调用。它们使用的表示术语更加面向计算机而不是要解决的问题。

编写良好的OOP程序通常有关于下面两部分的定义:

  • 用来表示问题空间概念的对象。

  • 发送给这些对象的用来表示在此空间内的行为的消息。

面向对象程序设计的优点是:对于设计良好的程序,通过阅读它可以很容易地理解其代码,并且许多问题都可以通过重用现有的类库代码而得到解决。

0 1
原创粉丝点击