《java编程思想》第四版 第1 章 对象入门(1.3-1.5)

1 . 3 实现方案的隐藏

为方便后面的讨论,让我们先对这一领域的从业人员作一下分类。从根本上说,大致有两方面的人员涉足面
向对象的编程:“类创建者”(创建新数据类型的人)以及“客户程序员”(在自己的应用程序中采用现成
数据类型的人;注释④)。对客户程序员来讲,最主要的目标就是收集一个充斥着各种类的编程“工具
箱”,以便快速开发符合自己要求的应用。而对类创建者来说,他们的目标则是从头构建一个类,只向客户
程序员开放有必要开放的东西(接口),其他所有细节都隐藏起来。为什么要这样做?隐藏之后,客户程序
员就不能接触和改变那些细节,所以原创者不用担心自己的作品会受到非法修改,可确保它们不会对其他人
造成影响。

④:感谢我的朋友Scott Meyers,是他帮我起了这个名字。

“接口”(Interface)规定了可对一个特定的对象发出哪些请求。然而,必须在某个地方存在着一些代码,
以便满足这些请求。这些代码与那些隐藏起来的数据便叫作“隐藏的实现”。站在程式化程序编写
(Procedural Programming )的角度,整个问题并不显得复杂。一种类型含有与每种可能的请求关联起来的
函数。一旦向对象发出一个特定的请求,就会调用那个函数。我们通常将这个过程总结为向对象“发送一条
消息”(提出一个请求)。对象的职责就是决定如何对这条消息作出反应(执行相应的代码)。

对于任何关系,重要一点是让牵连到的所有成员都遵守相同的规则。创建一个库时,相当于同客户程序员建
立了一种关系。对方也是程序员,但他们的目标是组合出一个特定的应用(程序),或者用您的库构建一个
更大的库。

若任何人都能使用一个类的所有成员,那么客户程序员可对那个类做任何事情,没有办法强制他们遵守任何
约束。即便非常不愿客户程序员直接操作类内包含的一些成员,但倘若未进行访问控制,就没有办法阻止这
一情况的发生——所有东西都会暴露无遗。

有两方面的原因促使我们控制对成员的访问。第一个原因是防止程序员接触他们不该接触的东西——通常是
内部数据类型的设计思想。若只是为了解决特定的问题,用户只需操作接口即可,毋需明白这些信息。我们
向用户提供的实际是一种服务,因为他们很容易就可看出哪些对自己非常重要,以及哪些可忽略不计。

进行访问控制的第二个原因是允许库设计人员修改内部结构,不用担心它会对客户程序员造成什么影响。例
如,我们最开始可能设计了一个形式简单的类,以便简化开发。以后又决定进行改写,使其更快地运行。若
接口与实现方法早已隔离开,并分别受到保护,就可放心做到这一点,只要求用户重新链接一下即可。

Java 采用三个显式(明确)关键字以及一个隐式(暗示)关键字来设置类边界:public,private,
protected 以及暗示性的friendly。若未明确指定其他关键字,则默认为后者。这些关键字的使用和含义都
是相当直观的,它们决定了谁能使用后续的定义内容。“public”(公共)意味着后续的定义任何人均可使
用。而在另一方面,“private”(私有)意味着除您自己、类型的创建者以及那个类型的内部函数成员,其
他任何人都不能访问后续的定义信息。private 在您与客户程序员之间竖起了一堵墙。若有人试图访问私有
30
成员,就会得到一个编译期错误。“friendly ”(友好的)涉及“包装”或“封装”(Package)的概念——
即Java 用来构建库的方法。若某样东西是“友好的”,意味着它只能在这个包装的范围内使用(所以这一访
问级别有时也叫作“包装访问”)。“protected”(受保护的)与“private”相似,只是一个继承的类可
访问受保护的成员,但不能访问私有成员。继承的问题不久就要谈到。

1 . 4 方案的重复使用

创建并测试好一个类后,它应(从理想的角度)代表一个有用的代码单位。但并不象许多人希望的那样,这
种重复使用的能力并不容易实现;它要求较多的经验以及洞察力,这样才能设计出一个好的方案,才有可能
重复使用。

许多人认为代码或设计方案的重复使用是面向对象的程序设计提供的最伟大的一种杠杆。

为重复使用一个类,最简单的办法是仅直接使用那个类的对象。但同时也能将那个类的一个对象置入一个新
类。我们把这叫作“创建一个成员对象”。新类可由任意数量和类型的其他对象构成。无论如何,只要新类
达到了设计要求即可。这个概念叫作“组织”——在现有类的基础上组织一个新类。有时,我们也将组织称
作“包含”关系,比如“一辆车包含了一个变速箱”。

对象的组织具有极大的灵活性。新类的“成员对象”通常设为“私有”(Private),使用这个类的客户程序
员不能访问它们。这样一来,我们可在不干扰客户代码的前提下,从容地修改那些成员。也可以在“运行
期”更改成员,这进一步增大了灵活性。后面要讲到的“继承”并不具备这种灵活性,因为编译器必须对通
过继承创建的类加以限制。

由于继承的重要性,所以在面向对象的程序设计中,它经常被重点强调。作为新加入这一领域的程序员,或
许早已先入为主地认为“继承应当随处可见”。沿这种思路产生的设计将是非常笨拙的,会大大增加程序的
复杂程度。相反,新建类的时候,首先应考虑“组织”对象;这样做显得更加简单和灵活。利用对象的组
织,我们的设计可保持清爽。一旦需要用到继承,就会明显意识到这一点。

1 . 5 继承:重新使用接口

就其本身来说,对象的概念可为我们带来极大的便利。它在概念上允许我们将各式各样数据和功能封装到一
起。这样便可恰当表达“问题空间”的概念,不用刻意遵照基础机器的表达方式。在程序设计语言中,这些
概念则反映为具体的数据类型(使用class 关键字)。

我们费尽心思做出一种数据类型后,假如不得不又新建一种类型,令其实现大致相同的功能,那会是一件非
常令人灰心的事情。但若能利用现成的数据类型,对其进行“克隆”,再根据情况进行添加和修改,情况就
显得理想多了。“继承”正是针对这个目标而设计的。但继承并不完全等价于克隆。在继承过程中,若原始
类(正式名称叫作基础类、超类或父类)发生了变化,修改过的“克隆”类(正式名称叫作继承类或者子
类)也会反映出这种变化。在Java 语言中,继承是通过extends 关键字实现的
使用继承时,相当于创建了一个新类。这个新类不仅包含了现有类型的所有成员(尽管private 成员被隐藏
起来,且不能访问),但更重要的是,它复制了基础类的接口。也就是说,可向基础类的对象发送的所有消
息亦可原样发给衍生类的对象。根据可以发送的消息,我们能知道类的类型。这意味着衍生类具有与基础类
相同的类型!为真正理解面向对象程序设计的含义,首先必须认识到这种类型的等价关系。

由于基础类和衍生类具有相同的接口,所以那个接口必须进行特殊的设计。也就是说,对象接收到一条特定
的消息后,必须有一个“方法”能够执行。若只是简单地继承一个类,并不做其他任何事情,来自基础类接
口的方法就会直接照搬到衍生类。这意味着衍生类的对象不仅有相同的类型,也有同样的行为,这一后果通
常是我们不愿见到的。

有两种做法可将新得的衍生类与原来的基础类区分开。第一种做法十分简单:为衍生类添加新函数(功
能)。这些新函数并非基础类接口的一部分。进行这种处理时,一般都是意识到基础类不能满足我们的要
求,所以需要添加更多的函数。这是一种最简单、最基本的继承用法,大多数时候都可完美地解决我们的问
题。然而,事先还是要仔细调查自己的基础类是否真的需要这些额外的函数。

1 . 5 . 1 改善基础类

尽管extends 关键字暗示着我们要为接口“扩展”新功能,但实情并非肯定如此。为区分我们的新类,第二
个办法是改变基础类一个现有函数的行为。我们将其称作“改善”那个函数。
为改善一个函数,只需为衍生类的函数建立一个新定义即可。我们的目标是:“尽管使用的函数接口未变,
但它的新版本具有不同的表现”。

转载请注明:原文地址 http://www.lingshu.xin/article/7a764f6f.html
这里写图片描述

猜你喜欢

转载自blog.csdn.net/jinghuayuanli/article/details/82258800