非计软专业的学生也能看懂的面向对象编程(《面向对象编程是怎样工作的》平野章/著 读书笔记)

编程语言的演化

首先在了解面向对象编程之前,我们需要理解为什么我们需要面向对象编程,在长久的编程语言发展史中,我们可以看出每一代语言的诞生都有其独特的特点,而且不是从天而降,而是以前一代语言为基础,解决前一代语言缺点为目的。


在这里插入图片描述
(GOTO语句即无条件跳转到程序中的任意标签的命令。)





在这里插入图片描述




在这里插入图片描述


但是结构化编程并没有解决全局变量问题和可重用性差的问题,于是面向对象编程应运而生。

面向对象是什么

面向对象的英文是 Object Oriented,直译为“以对象为中心”。

在面向对象普及之前,主流的开发方法是“面向功能”的,具体地说,就是把握目标系统整体的功能,将其按阶段进行细化,分解为更小的部分。如果采用面向功能的开发方法来编写软件,当规格发生改变或者增加功能时,修改范围就会变得很广,软件也很难重用。

面向对象技术的目的是使软件的维护和重用变得更容易,其基本思想是重点关注各个构件,提高构件的独立性,将构件组合起来,实现系统整体的功能。通过提高构件的独立性,当发生修改时,能够使影响范围最小,在其他系统中也可以重用。

面向对象起源于 1967 年在挪威设计的 Simula 67 编程语言。该语言拥有类、多态和继承等以往的编程语言中没有的优良结构,被称为最早的面向对象编程语言(Object Oriented Programming language,OOP )。

后来,艾伦·凯率领的团队开发的 Smalltalk沿用了该结构,确立了“面向对象”的概念。此后,具有相同结构的C++、Objective-C、Java、C# 和 Ruby 等诸多编程语言相继被开发出来,并延续至今。

面向对象难以理解的三大原因

1.术语洪流
2.比喻滥用
3.“一切都是对象”综合症


面向对象编程的三大要素

类(封装)

类是面向对象的最基本的结构,与其对应的概念是实例 。
类的英文是 class,含义是“种类”“类型”等“同类物品的集合”。
实例的英文是 instance,含义是“具体的物”。
类指类型,实例则指具体的物,二者的关系就相当于数学集合论中的集合和元素一样。

类的功能是汇总、隐藏和“创建很多个”。
①“汇总”子程序和变量。
②“隐藏”只在类内部使用的变量和子程序。
③ 从一个类“创建很多个”实例。


多态(polymorphism)

笔者认为该书对于多态的解释并不够清晰易懂,于是自行查阅资料进行解释。

在编程语言当中,多态是指用相同的接口去表示不同的实现。
多态可以让程序员针对抽象而不是具体实现来编程,这样的代码会有更好的可扩展性。
当然为了使用多态你需要进行抽象,也就是定义一个接口让不同的对象去实现,这样从这个接口的角度看各个对象就一样了,因此可以以一致的方式来使用这些不同类型的对象。

即使给出定义,多态可能仍然难以理解,下面借用知乎看到的一个例子(https://zhuanlan.zhihu.com/p/191617344)

假设代码中有三个class: 自行车(Bicycle)、汽车(Car)和卡车(Truck),这三个class分别有这样三个实现:Ride()、Run()、Launch(),实际上都是让它们发动起来。

// 实现
Bicycle bicyle = new Bicycle();
Car car = new Car();
Truck truck = new Truck();

// 使用部分
bicyle.Ride();
car.Run();
truck.Launch();

在这样的代码实现中如果Bicyle的接口修改了,那么使用部分的代码同样需要修改,这是程序员所不想看到的。尽管自行车、汽车、卡车是不同的东西,但是当我们将其抽象为交通工具后就可以一视同仁的对待它们,这就是多态。

如果根据多态的思想对代码进行如下更改,代码是不是一下就简洁多了,最棒的是不管实现部分代码怎么改动都不会影响到使用部分的代码,这就是设计模式当中所谓的"只针对抽象编程,而不是针对实现编程"。

class Vehicle {       // 新增抽象类
    void Run() {}
}

class Bicycle: Vehicle {
    void Run() {......} // Ride修改为Run
}
class Car: Vehicle{
    voie Run() {......} // 无需改动
}

class Truck: Vehicle {
    void Run() {......}  // Launch修改为Run
}

因此,换个角度而言,多态,就是重载和重写/覆盖
重载发生在一个类中,重写发生在子类,意思就是子类重写父类相同名称的方法。
重载的特性,方法名相同,返回类型,传入方法的参数不同(包括个数和类型)。
重写的特性,方法名相同,返回类型,参数均相同,必须发生在子类。


继承

继承是“整理相似事物的类的共同点和不同点的结构”,相当于数学集合论中的全集和子集。
简单地说,继承就是“将类的共同部分汇总到其他类中的结构”。通
过利用该结构,我们可以创建一个公用类来汇总变量和方法,其他类则可以完全借用其定义。
超累相当于“父类”。
在这里插入图片描述


实例变量

实例变量的特性:
① 能够隐藏,让其他类的方法无法访问。
② 实例在被创建之后一直保留在内存中,直到不再需要。

在这里插入图片描述

也就是说,实例变量融合了局部变量能够将影响范围局部化的优点以及全局变量存在期间长的优点。
我们可以将实例变量理解为存在期间长的局部变量或者限定访问范围的全局变量。

进化的OOP结构

包是进一步对类进行汇总的结构。
包只是进行汇总的容器,它不同于类,不能定义方法和实例变量。
它类似于文件系统中的目录(文件夹),除了类之外,还可以存储其他包,从而创建层次结构。
虽然目录只是存储文件的容器,但是通过给目录命名,并在其下存储文件,文件管理就会变得非常轻松。反之,我们再想象一下没有目录、所有文件都存储在根目录下的状态,使用起来很不方便 。
除此之外,包还具有防止类名重复的重要作用。

异常

如果用一句话来概括异常,那就是:采用与返回值不同的形式,从方法返回特殊错误的结构。
异常的出现是为了解决两个问题:
第一个问题是需要在应用程序中执行错误码的判断处理。
第二个问题是判断错误码的相同逻辑在子程序之间是连锁的。
异常就是用于解决以上问题的结构。
异常结构会在方法中声明可能会返回特殊错误。这种特殊错误的返回方式就是异常,其语法不同于子程序的返回值。

垃圾回收

删除内存中不再需要的实例是系统提供的专用程序——垃圾回收器的工作。
这种结构不将容易出错的内存释放处理作为编程语言的语法提供,而是由系统自动执行。


OOP的运行机制

编译器方式和解释器方式

编译器方式是将程序中编写的命令转换为计算机能够理解的机器语言之后再运行的方式。将命令转换为机器语言的程序称为编译器。

解释器方式则是将源代码中编写的程序命令边解释边运行的方式。这种方式能读取源代码并立即运行,因此不需要编译器。如果程序有语法错误,运行时就会发生错误。

编译器方式的优点是运行效率高。计算机直接读取机器语言进行动作,没有解释程序命令的多余动作,因此运行速度快。而缺点是运行前会耗费一些时间。这是因为程序无法立即运行,需要先进行编译。另外,在发现错误的情况下,还需要将错误修正后才可以运行。

解释器方式的优点是可以立即运行。在使用这种方式的情况下,编写完程序后就可以立即运行一下查看结果。另外,该方式还有一个优点,就是可以确保不同平台(机器、操作系统)之间的兼容性。

如果将机器语言的代码发布到其他环境的硬件中,通常是无法运行的 。不过,解释器方式会匹配机器环境进行解释、运行,因此无须对程序进行修改,就可以在多种环境下运行。该方式的缺点是运行速度慢,与编译器方式的优点正好相反。


中间代码方式

这种方式首先使用编译器将源代码转换为不依赖于特定机器语言的中间代码,然后使用专门的解释器来解释中间代码并运行。
这样做是为了汲取两种方式的优点。既可以将同一个程序发布到不同的机器上,又可以发扬编译器运行效率高的优点。
得益于硬件的进步和各种机器共存的互联网环境,这种方式最终得以实现。采用中间代码方式,同一个程序可以在不同的运行环境中高效地运行。


虚拟机

由于中间代码的命令是不依赖于特定运行环境的形式,所以 CPU 无法直接读取并运行。因此,我们需要一种解释中间代码并将其转换为 CPU 能够直接运行的机器语言的结构。这种结构一般被称为虚拟机。


进程,线程和CPU多线程运作

作为一个计算机术语,线程的意思是“程序的运行单位”。
进程表示的单位比线程大,一个进程可以包含多个线程。

虽然我们在这里采用了“多个线程同时并发运行”的说法,但是严格来说,这样的表述并不准确。实际上,计算机的心脏——CPU 在某个时刻只能执行一个处理。那么,我们为什么说并发处理能够实现呢?

这是因为 CPU 会依次循环执行多个线程的处理。当 CPU 执行线程的处理时,并不是将一个线程从开始一直执行到结束,而是仅在非常短的规定时间(通常以毫秒为单位)内执行。

在执行完这段规定时间后,即使该线程的处理还未完成,计算机也会暂时中断处理,转而处理下一个线程。下一个线程也是如此,仅执行规定的时间,然后马上跳转到下一个线程。

虽然 CPU 实际上是在交替执行多个线程的处理,但是由于其处理速度非常快,所以在计算机的使用者看来就像在同时执行多个作业一样。

这种能够同时运行多个线程的环境称为多线程环境。这种多线程功能基本上都是作为操作系统的功能提供的。

(不过,在一台计算机中搭载多个 CPU 的多处理器结构的情况下,有多少个 CPU,就可以并发执行多少个处理)


使用静态区,堆区和栈区进行管理

程序的内存区域分为静态区、堆区和栈区三部分。

静态区从程序开始运行时产生,在程序结束前一直存在。之所以称为“静态”,是因为该区域中存储的信息配置在程序运行时不会发生变化。

静态变量,即全局变量和将程序命令转换为可执行形式的代码信息就存储在该区域中。

堆区是程序运行时动态分配的内存区域。“堆”的英文“heap”有“许多”“大量”之意。由于在程序开始运行时预先分配大量的内存区域,所以命名为堆区。

堆区是在程序运行过程中根据应用程序请求的大小进行分配的,当不
再需要时就将其释放。
最好为堆区划分一块较大的空区域,以便有效利用内存。在多个线程同时请求内存时,也需要保持一致性,因此一般由操作系统或虚拟机提供管理功能。实际的分配和释放处理都是通过该管理功能进行的。

栈区是用于线程的控制的内存区域。堆区供多个线程共同使用,而栈区则是为每个线程准备一个。各个线程依次调用子程序(在 OOP 中是方法)进行动作。栈区是用于控制子程序调用的内存区域,存储着子程序的参数、局部变量和返回位置等信息。

栈区这一名称来源于其使用方法。“栈”的英文“stack”有“堆积”的含义。栈区中不断堆积新的信息,使用时从最上面放置的信息开始使用,这种用法称为后入先出(Last In First Out,LIFO)。子程序调用是嵌套结构,在调用的子程序的处理结束之前,再次调用子程序。通过这种方式,可以高效地使用内存区域。
在这里插入图片描述

在这里插入图片描述

OOP 的特征在于内存的用法

1.每个类只加载一个类信息
2.每次创建实例都会使用堆区
3.在变量中存储实例的指针
4.复制存储实例的变量时要多加注意
5.多态让不同的类看起来一样
6.根据继承的信息类型的不同,内存配置也不同
7.孤立的实例由垃圾回收处理


对OOP进化的总结

OOP 是一种手段,其目的不在于被人们使用,而是提高程序的质量、可维护性和可重用性。
切记我们的目的是编写出高质量、易于维护和可重用的程序,面向对象只是实现该目的的一个手段而已。
我们首先要思考怎么做才能使程序更容易维护和重用。然后考虑使用三种基本结构和公用子程序来进行实现。如果这样还不够,那就轮到类、多态和继承大显身手了。



OOP带来的软件重用和思想重用

类库是 OOP 的软件构件群

类库(library class)中的“类”就是指 OOP 结构中的类,而类库则“集合了很多具有通用功能的类”。

这种通用的类库过去就存在,称为“函数库”或者“公共子程序库”等。由于 OOP 中描述软件的单位是“类”,所以也就称为“类库”。

不过,类库并不只是简单地将描述软件的单位由子程序改为类。类库和函数库的最大不同就在于使用方法的进步。

在传统编程语言中,可重用的构件只有子程序。因此,应用程序中只是调用子程序库进行使用。
而 OOP 中拥有类、多态和继承等结构,因此,并不只是简单地从应用程序进行调用,还可以执行下述操作。

①从库中的类创建实例,汇总方法和变量定义进行使用(利用
类)。
②可以将库调用的逻辑替换为应用程序固有的处理(利用多态)。
③向库中的类添加方法和变量定义,来创建新类(利用继承)。


框架

框架(framework)一词在英文中的含义是“结构”“骨架”,除了可被用于软件领域,有时还表示用于解决商业课题的工具和思考方法。

在软件开发领域,框架的定义有些模糊,但大致可以分为两种情况:

1.作为“总括性的应用程序基础”这种比较笼统的含义使用;
2.指代针对特定目的编写的可重用构件群。

前者主要用来表示开发和运行环境、商业产品的概念,比如指代使用网络技术的应用程序基础的“Web 应用程序框架”、微软公司提供的开发和运行环境“.NET 框架”等。

后者是指使用特定编程语言编写的具体的软件构件群。现在用很多语言编写的框架都是开源的。在 Java EE 中,主要提供 Web 应用程序的画面控制功能的 Struts、轻量级容器 Spring、用于数据库控制的 iBATIS 和 Hibernate 等都已成为事实标准。

框架是应用程序的半成品

我们基于后一种定义——使用特定编程语言编写的具有特定目的的可重用构件群,来介绍一下框架的相关内容。

基于该定义,框架和类库都指可重用的软件构件群,一般会根据目的和使用方法区分使用二者。通常在称为类库的情况下,只是指利用OOP 结构创建的可重用构件,并不限制其目的和使用方法。但在称为框架的情况下,则并不只是指利用 OOP 创建的类库,还指用于特定目的的应用程序的半成品。

另外,从应用程序中的使用方法上来说,并不是像传统的函数库那样简单地进行调用,而是从框架来调用应用程序。也就是说,在框架端预先提供基本的控制流程,在应用程序中嵌入个别的处理。

独立性较高的构件:组件

组件的一般定义如下所述"
1.粒度比 OOP 的类大
2.提供的形式是二进制形式,而不是源代码形式
3.提供时包含组件的定义信息
4.功能的独立性高,即使不了解内部的详细内容,也可以使用说

微软公司开发的 Visual Basic 的控件首先将“组件”这一术语渗透到业界的。具体来说,就是提供控制 GUI 的独立性较高的构件,支持在开发环境中进行属性设置和拖曳操作,通过组合这些构件,从而轻松地创建出画面。该技术使用方便,并且出现于 Windows真正普及时期,时间点也非常好,因此得以迅速普及。

设计模式

设计模式(design patterns)就是“设计的模式”。具体来说,就是不依赖于编程语言和应用程序的应用领域,对在各种情况下反复出现的类结构进行命名,形成模式。与前面介绍的可重用构件群一样,设计模式不采用具体的源代码或者运行形式的模块等方式,而是从中提取出优秀的思想,总结成文档。

设计模式是前人为了创建便于功能扩展和重用的软件而研究出的技术窍门集,对典型的解决方法以及使用它们时需要考虑的要点等都进行了汇总。

设计模式被大家广泛认知始于 1995 年 Design Patterns: Elements of Reusable Object-Oriented Software 一书的出版。该书由来自不同国家的具有不同技术背景的 4 名技术人员精诚合作而成。人们将这 4 名技术人员亲切地称为 GoF(Gang of Four,四人组) ,将他们共同发表的 23 种设计模式称为 GoF 设计模式。

在这里插入图片描述

UML:查看无形软件的工具

UML 是 Unified Modeling Language(统一建模语言)的缩写,是用图形表示软件功能和内部结构的统一的表示方法。

在 UML 被制定之前就已经有了许多面向对象方法论,每种方法论的图形表示方法也是各式各样。直到 20 世纪 90 年代中期,三位方法学家葛来迪·布区(GradyBooch)、詹姆士·兰宝(James Rumbaugh)和伊瓦尔· 雅各布森(Ivar Jacobson)统一了表示方法,形成了 UML。

UML 是一个固定形式的世界标准,它将软件功能和内部结构表示为二维图形。如果用一句话来描述UML,就可以说“UNL 是查看无形软件的工具”。在实际的系统中,程序有几十万行之多,规格说明书等文档也多达几百页。如果使用 UML 图,就可以从庞大的信息中提取出重要的部分,表示为逻辑清晰且直观的形式。

设计的目标

最重要的是按照需求规格说明正确运行。
第二重要的目标,在过去应该是运行效率,现在随着硬件的发展,可维护性和可重用性变得更加重要。

易于维护和重用的软件结构的目标汇总为如下三点。
①去除重复。
②提高构件的独立性。
③避免依赖关系发生循环。



常用的应用程序

业务应用程序

企业等的业务活动中使用的系统。诸如出货、订货、库存管理、制造业的生产管理、银行的账目系统、会计和人事等各种系统。购物网站也可以归为此类。

嵌入式软件

管理电器及各种设备的软件。因为软件是在嵌入装置的 CPU 上运行的,所以这样命名。

单机应用程序

是指在个人计算机或便携式终端等上面运行的软件。比如电子邮件、浏览器、文字处理软件、电子表格软件、进度管理软件和游戏软件等都属于单机应用程序。之前单机应用程序大多只在客户端环境中运行,但近来通过网络与服务器通信的应用程序也不断增多。除此之外,还有在底层支持这些应用程序的基础软件。

基础软件

诸如 Windows 和 Linux 等操作系统,以及管理数据库处理和通信控制的中间件等。



面向对象的下一代技术——函数式语言是怎样工作的

函数式语言的 7 个特征:
特征 1:使用函数编写程序
特征 2:所有表达式都返回值
特征 3:将函数作为值进行处理
特征 4:可以灵活组合函数和参数
特征 5:没有副作用
特征 6:使用分类和递归来编写循环处理
特征 7:编译器自动进行类型推断

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值