第03章 面向对象的核心特性1Word文档下载推荐.docx
《第03章 面向对象的核心特性1Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《第03章 面向对象的核心特性1Word文档下载推荐.docx(11页珍藏版)》请在冰豆网上搜索。
这三个好处是软件工程永恒的追求。
3.0面向对象是适合人的自然思维习惯的编程方法(补充教材)
3.0.1范式及编程语言
世界上有很多的编程语言,可以按照不同的标准对其分类,第二章在讲到JAVA数据类型时,曾将编程语言按类型分为强类型的和弱类型的。
下面对编程语言作另一种分类:
按范式分类。
范式的英文单词是paradigm。
它的原意大致是典范、范例、示例的意思,为了帮助我们理解某些英文动词的用法,老师会给一些例句(paradigm、example),在所有的计算机编程书籍中,都有大量的例程(范例)。
有些计算机书籍中,将paradigm称为“范例”——指一种示范性的模型或例子,它提供了一种组织信息的形式。
范式的概念是1962年,美国科学史学家和科学哲学家托马斯·
库恩(ThomasS.Kuhn)出版了著名的《TheStructureofScientificRevolutions》一书提出的,其核心——范式论在自然科学家中引起强烈的共鸣,并波及社会科学的广泛领域。
在计算机领域我们也常使用范式的概念,比如著名的关系数据库的第一、二、三范式。
范式包括三个方面:
(1)范式建立在科学理论体系内。
(2)范式运用该理论体系的心理认知因素。
(3)范式指导和联系上述两者(理论体系和心理认知)的自然观。
范式观念对于学习和使用OOP十分重要。
在学习OOP时,许多人自然而然偏重于技术层面,能够把OO语法搞得清清楚楚,但忽视自然观和心理认知因素。
这常常使他们在采用OO思想的过程中,产生无用感、被迫感、挫折感、逃避愿望。
现有的编程语言,按照范式可分为以下四类:
(1)命令的或过程式,如C语言,Pascal语言。
(2)声明的或基于逻辑的,如Prolog语言。
(3)函数的,如LISP、ML语言。
(4)面向对象的,如Java、C#,C++等
与语言门派相对应的就是所谓的四大编程范式(Programmingparadigm)或曰四大计算模型。
其中第2类----声明的范式(declarativeparadigm)多被用于知识表达和推理的应用中,似乎暂时离我们远了点因次本课不讨论它。
(1)命令范式
命令的语言或过程式语言(imperativeorprocedurallanguages)是最为传统的编程语言。
从机器语言、汇编语言到早期的某些高级语言(如BASIC、Fortran、Cobol及C等),都属于该范式。
过程式语言是命令驱动(command-driven)或者说是面向语句的(statement-oriented)语言。
其基本概念是机器状态(machinestate)——计算机中所有内存区域的所有值的集合。
一个程序由一系列的语句组成,而每一个语句的执行导致计算机的内存的一个或多个区域的值发生变化,也就是说,机器进入了一个新的状态。
过程式语言的语法(syntax)一般有这样的形式:
statement1;
statement2;
……
内存是一些小盒子的集合,一个语句的执行(例如两个变量相加得到第3个变量)就是访问内存区域(两个小盒子),通过某种方式合并区域的值并将结果保存到某一个区域(小盒子)中。
这是编程者非常容易接受的第一印象,这一计算模型追随的是这种事实:
计算机硬件按顺序执行指令(instruction)。
计算机就是这样运作的,我们的编程语言也应该追随,因此,大量的传统编程语言遵循这样计算模型,广泛使用的语言,如C、Fortran、Cobol、Pascal等都支持这一计算模型。
从编程方式和思想上,传统的命令范式把编程过程定义为一个命令序列的开发,这些命令操作数据而产生所期望的结果。
事实上它是以CPU的“取得-解码-执行”循环为基础。
其核心是“解决一个问题所需要的算法是什么?
”或者说“什么是其算法,为了解决一个问题”。
其代表性口号是“程序=算法+数据结构”。
其基本的程序结构是:
顺序、分枝、循环,这三种结构加上一些必要的数据类型,能解决任何算法问题。
命令范式语言有很多是高级语言,但这些高级语言是带有浓厚的机器思维色彩。
但无论如何命令范式是几乎所有编程语言的基础,现在面向对象范式的语言,如JAVA也保留了命令范式的语法,这些语法几乎都在教材第2章。
(2)函数范式
函数式语言(functionalorapplicativelanguages)是不同于过程式语言的一种计算,它不注重于机器状态的变化,而是关注程序表现出来的功能。
也就是说,为了访问变量的初始化数据并以某种方式合并它们从而得到结果,我们必须找到一些函数(function)应用在机器初始状态上。
其语法一般有这样的形式:
functionn(……function2(function1(data))……)
函数式语言的典型代表是LISP,函数范式(functionalparadigm)将编程活动视为构造“黑匣子”,黑匣子的工作方式是:
参数输入代码块结果输出。
即参数被传入某个处理过程,最后返回计算结果,这就是其称为函数范式的原因,黑匣子如同数学上的函数:
Y=F(x1,x2,x3…),x是函数的自变量(也可叫参数),F是函数名字,Y是函数的运算结果。
函数式语言把编程过程视为一些函数的开发,所构造的函数是预先开发的简单函数的嵌套联合体,所有的函数作用于初始数据和中间数据直到最后一个函数能够计算出结果。
显然,函数范式是天然的模块方式(modularapproach)。
函数范式语言提供了编程模块的封装(函数级别的“黑匣子”)和复用(函数可以重复调用),这是非常重要的程序设计思想,因此,命令范式和面向对象范式的语言也继承了这种思想。
在C/C++语言里,我们有函数和类的成员函数;
在JAVA、C#等纯面向对象语言里,函数改了个称呼:
叫“方法”,方法也不是独立的,而是类的一部分。
(3)面向对象范式
面向对象范式是命令的范式和函数范式的自然产物,是它们的继承与发展。
面向对象范式超越了算法的界限,应该视为软件的整体设计思想,这种思想不再是机器思维的模仿,而是达到了与人的自然思维方式的基本一致。
这种思想是建立在对象、类、属性、方法、封装、重载、实例、继承、多态等概念之上的。
下面逐一介绍这些概念及其与JAVA语言对应的语法表示。
计算机语言的发展趋势是:
越来越接近人的自然思维习惯。
面向对象编程,是再目前成熟的编程方法中,最接近人的自然思惯的编程方法,的其代表性口号是“程序=(对象的)复用+扩展”。
3.0.2对象和类
(1)对象、属性、方法、类
面向对象思想起源于何时?
应该说从人类具有抽象思维能力的那一天就有了,人的“自然的、本能的”思维方式就是面向对象的。
系统的总结面向对象的思想并非起源于软件工程界,而是与哲学有很大渊源。
首先要提一下的是维特根斯坦(1889-1951),他是上世纪乃至人类哲学史上最伟大的哲学家之一。
他于1922年出版了一本著作——《逻辑哲学论》(TractatusLogico-Philosophicus)在该书中,他阐述了一种面向对象的世界观,或者说一种面向对象认识世界的观点,这种世界观对面向对象的软件方法学有很大影响。
其次,培根(FrancisBacon,1561—1626)他提出了科学认识的方法──归纳法,OO分类体现了这种方法。
还有达尔文的进化论,与OO的继承思想如出一辙。
总之,在上世纪在六七十年代后,软件工程界受到他们的启示,使面向对象终于由一种哲学思想沉淀到软件技术的层面上来,成为计算机业界的宠儿,这就是面向对象(OO,Object-Oriented,港台用语叫物件导向)的由来。
什么是对象?
对象是指客观世界一切可被呈现的或被感知的事物。
首先对象是物理实体,比如我们日常生活中常说“某某党员发展对象,恋爱对象,帮助对象,扶贫对象”都是指物理实体;
其次对象还可以是任一类概念实体,例如教师对学生成绩进行研究分析,学生成绩是个概念实体。
总之,世间万物,只要被你观察上了研究上了,皆对象。
当我们用“自然的,本能的”思维方式对一个对象观察研究的时侯,会发现对象包含两方面的特征:
一方面是事物的静态特性:
如尺寸,形状等,静态特性一般描述这个事物“是什么”,一般是名词性的,我们这一类的特征叫对象的属性(attribute);
另一方面是事物的动态特性(行为特性):
动态特性一般描述这个事物“干什么(操作)”,一般是动词性的,我们这一类的特征叫对象的方法(method)。
比如说我们把大学生张三作为一个对象观察,那么他的相貌、身高、体重、年令等是对象张三的属性,他吃饭、睡觉、走路、学习、写字则是对象张三的方法。
[提示:
在C++里,属性叫成员变量,方法叫成员函数。
]
下面我们对大学生张三建立一个“对象模型”,用伪代码描述:
Object张三//对象的名字是张三
{
name=“张三”;
age=21;
school=“山大威海分校”;
。
//上面是对象张三的静态描述,是张三的属性
Study(“JAVA编程”)
Play(“篮球”)
//上面是对象张三的动态描述,是张三的方法
}
这个模型,只是对大学生张三这个具体的对象的描述,稍加观察我们会发现,张三所具有的属性和方法在大学生里具有普遍意义,或者说,张三是大学生这一类对象的一个个体对象。
将上面的“对象模型”抽象一下,改造为具有普遍意义的“一类对象”:
class大学生//用class代表一类对象
Stringname;
//不指定name的具体值,而是将name抽象为String
intage;
//不指定age的具体值,而是将age抽象为int
Stringschool;
//上面是大学生类别的静态描述,是大学生类的属性
Study(String课程名)//不指定具体课程名,而是将课程名抽象为String
Play(String运动项目)
//上面是对大学生类别的动态描述,是大学生类的方法
这个类的模型是抽象的,因此有普遍意义,对所有大学生都适合。
当我们需要“对象张三”时,只需对上述模型的属性赋值,对方法使用不同参数即可,这个过程我们叫作类的实例化,即从大学生类里面实例化出具体的“对象张三”。
比如:
Object张三
{name=“张三”;
我们看到,这个实例化出来的新的“对象张三”,与开始为大学生张三建立的“对象模型”是一样的。
我们走过了这么一个从个别到普遍,再从普遍到个别的过程:
(1)先对个体的大学生张三建立“对象模型”;
(2)将上步的对象模型抽象成“大学生类”,使其具有普遍意义;
(3)从类里面实例化出来新的“对象张三”,该对象与
(1)的模型相同。
第
(2)步“大学生类”的抽象是非常关键的,它的真正价值不在于为张三这个具体对象服务,而是在于,“大学生类”可以作为大学生这一系列的个体对象的模版,通过对这个模版的属性赋不同的值、方法给不同的参数,可以实例化出多个不同的大学生对象,换句话说,“大学生类”是可以重复使用(简称复用)的,而复用正是软件工程永恒的追求之一。
Object李四
{name=“李四”;
age=20;
school=“哈工大威海分校”;
Study(“C#编程”)
Play(“足球”)
Object王五
{name=“王五”;
age=22;
school=“山大总校”;
Study(“C++编程”)
Play(“排球”)
通过前面的讲述,我们了解了对象、属性、方法、类的概念,小结如下:
对象:
客观世界的一个具体事物。
类:
对客观世界的具有相同特征的一类具体事物的抽象。
从类中可实例化出相同特征的不同对象。
类是抽象的、可以复用的软件模块。
属性、方法:
对象或类对事物的静态描述(名词性的)叫属性,动态描述叫方法。
我们很容易想到,如果用程序语言描述,属性可以用变量或常量代表,方法可以用函数代表。
或者说,一个类(或对象)是一些变量(或常量)和操作这些变量(或常量)的函数的集合。
对象、属性、方法、类是OO最基本的概念。
其最基本的特征是:
从个别对象的行为(包含属性和方法)抽象出一类对象的的行为,再反过来作用到个别对象。
(2)对于类的研究
下面我们把重点转入对类的研究。
通过前面对“大学生类”的建模,我们学会了一种对客观事物分类的方法,从具体到普遍的抽象。
实际上这种抽象建模能力是我们与生俱来的,我们在很小的时候就会把不同的人归类为大人、小孩、好人、坏人、男人、女人,把车辆归类为火车、汽车、自行车、卡车、轿车。
我反复强调,OO是非常接近人的自然思维的编程方法,证据之一就是我们幼年的时侯就会对客观事物进行分类。
按科学的观点,人类研究自然主要有两种方法:
逻辑演绎和分类归纳。
我们对以数学为代表逻辑演绎方法比较熟悉,而对分类研究则相对陌生---虽然我们与生俱来就有分类能力。
面向对象程序设计最重要的技巧就是分类,你面对一个大型项目首先要做的就是分类。
古人很早就开始对自然界进行分类研究,比如中国的本草纲目。
现代科学意义上的分类学是英国科学家培根(1561-1626)创建的。
分类研究的主要特征是把若干事物按其属性和行为分门别类进行研究。
比如哺乳动物类里有马,鲸鱼等等。
我们认为,在自然界里,存在很多有相同属性和方法的对象,我们可以把它们抽象成一个类。
除了前面例举的大学生类,还可以找出很多例子,例如:
各种品牌电视机都有“商标”,“尺寸”属性,和“选频道()”方法。
,我们把所有电视机抽象成一个类“电视机”。
因为类是对对象的抽象,所以类也有方法,属性等概念。
我们知道对象是客观世界的实体(物理实体和概念实体),但类只是一个抽象概念,不是实体。
既然类不是实体,就不能直接作用到客观世界。
我们必需把类从抽象概念变成实体,才能作用到客观世界。
这个过程是:
把对象先抽象成类,再把类实例化成对象。
类的存在价值在于,可以以类为模版实例化出无数同类对象,而这些对象的外观各不相同(就像前面讲过的从“大学生类”实例化张三、李四、王五对象一样,他们的外观是不一样的,但都是同类对象)。
类究竟是什么?
主要有两种解释:
●一种观点认为类是对一类对象的抽象,前面已讲过了;
●另一种观点认为类是一种聚合的、抽象的数据类型,再观察前面讲过的用伪代码描述的“大学生类”:
Study(String课程名)
我们看出,这个类的属性集由String、int等抽象数据类型组成(注意:
编程语言的整型、浮点型、字符型、布尔型等基本数据类型也是抽象数据类型,想想为什么它是抽象的?
),类的方法的参数也是抽象数据类型,我们可以认为类也是一种抽象数据类型,类聚合了多种抽象数据类型。
一个实际的面向对象软件系统,有很多个类组成,类相互间构成各种复杂的关系。
对类的研究及相应的JAVA语法,贯彻了教材第三章的绝大部分。
3.0.3类的封装性
前面学习了类、对象、属性和方法的概念,这些概念对所有面向对象编程语言都是通用的。
下面介绍另外一个通用概念:
类的封装性。
面向对象有三个最主要的特性:
封装、继承和多态。
下面讨论类的封装性,也就是面向对象的封装性。
什么叫封装?
我们在日常生活中,面对较复杂的客观事物,往往习惯于个它“打个包”,将其“黑箱化”,也就是说,很多事物,我们不太愿意也没有必要知道其“内在”的特性,我们只需了解其“外在”的特性就够了。
比如对一台电视机,我们只需知道其外观尺寸、遥控器的使用就可以以,没有必要知道电视机的内部构造。
对一个用户而言,电视机就是一个“黑箱”,用户通过这个“黑箱”的外在特性与其交互。
用户按下遥控器的一个按扭向“黑箱”输入些(比如切换频道),“黑箱”执行一些用户不必要知道的内部动作,然后“黑箱”输出些什么(比如屏幕画面转到另一个频道)。
对一个人来说,周围的客观事物绝大多数都是这样的“黑箱”---你的电脑、你的MP3、你周围的同学、老师。
我们所处的环境是如此的复杂多变,我们只能通过“黑箱”简化对客观事物的理解。
对于软件工程师,当他们在构造一个大型系统时,也处于复杂多变的环境,他们要合作工作,要彼此交流,要复用彼此的程序,他们不可能也没有必要对系统的每一个细节都了解,他们必需将系统分解成一个个“黑箱”,将系统的复杂度简化到能够理解、驾驭的水平。
软件工程师将系统“黑箱化”的另一个巨大好处是,可以促进软件的复用,A工程师将其作品封装成“黑箱”----其内在特性或许很复杂而其外在特性则很简单,这样B、C、D。
工程师就很容易重复使用A工程师的作品,他们只需了解作品的外在特行就可以了。
JDK---JAVA的类库就是由许多封装成类的“黑箱”组成的,这些由一流的软件工程师开发出来的作品,被全世界数百万JAVA程序员重复的使用。
我以前说过,复用是软件工程永恒的追求,软件业作为一个工业产业,其发展动力主要来源两点:
创新和复用,这与其它的工业产业没有区别,复用是创新的基础,创新则是复用的继承,两者结合就是可持续性发展。
好的复用技术能使软件工程师更容易分享彼此的作品,面向对象就是目前最好的软件复用技术,其出发点就是面向对象的封装。
我们从面向对象以前的封装技术----函数谈起。
以前讲过编程语言的函数范式,函数范式(functionalparadigm)将编程活动视为构造“黑箱”,黑箱的工作方式是:
函数范式语言提供了编程模块的封装(函数级别的“黑匣子”)和复用(函数可以重复调用),这是非常重要的程序设计思想。
面向对象范式是函数范式的发展。
已经学习了类的写法(伪代码):
class类名
//类的属性集
//类的方法集
}
类是这样一个封装,它封装了对象的动态特性(类的方法集),以及动态特性所共同操作的一组数据(类的属性集)。
或者说类高度内聚了一类对象的特征。
我们注意到在面向对象范式中,函数只是类的一个成员,也就是说类的封装粒度要比函数大的多,因此类的可复用性就有可能更高。
但是类的封装复杂度仍然很高,一方面一个类里面可能有很多的属性和方法,另一方面不同的类之间可能存在复杂的关系,下面考虑以下问题:
假定有5个类,每个类的外在特性数(暂称为复杂度)分别是10,20,30,40,50。
如果每个类之间都双向关联,则总复杂度最多可达到10*20*30*40*50=1千2百万,这已经超出常人可以控制的范围了。
必需减化。
简化复杂度主要有两种办法:
●减少每个类的外在特性数,比如上例每个类的外在特性均减少一半,则总复杂度最多可达到5*10*15*20*25=37万5千。
●去掉类的双向关联,将类的双向关联改为单向关联,则总复杂度由乘法变为加法。
这是三年级以后才能学到的设计技巧。
为了减少单个类的外在特性。
面向对象编程语言主要提供了两种语法:
类的方法重载,以及类成员(属性和方法)私有化。
(1)方法的重载
我们考虑这样一个问题:
描述一个学生的学习,这是一个动词性的对象方法,它的基本特征是学习,只是学习科目不同而已,是用多个方法描述好呢?
学语文(),学数学(),学物理(),学化学(),。
还是用一个方法描述好?
学(语文),学(数学),学(物理),学(化学)。
很显然后一种描述更合理,它可以大大简化对象的外在特性,让类的用户感觉是在使用一个方法做很多事,而实际上类的设计者用同一个方法名重写了很多个函数。
我们把一个类内,多个相同行为特征的方法,用一个方法名,不同的方法参数,表示出来,叫方法的重载。
(2)类及其成员的访问权限(信息隐藏)。
在一个类内的属性和方法,有一些仅供类的内部使用,这些属性和方法可以定义为类的私有成员(private),不允许外部访问,这是类的封装性的一个重要特征:
信息隐藏。
另一部分属性和方法允许外部对它们的访问,可以定义为类的公有成员(public)。
对类成员进行私有化/公有化处理,从类的外部用户角度看,类的复杂度大大减少,同时也有利于类的安全。
信息隐藏也是符合人的自然思维习惯的,人有一种本能的自我防护意识,与外界交流只有一个很小的接口,自身的大量信息都被隐藏了。