01.面向对象设计思想
目录介绍
- 01.看一个演变案例
- 1.1 一个案例介绍
- 1.2 普通过程思维
- 1.3 使用面向对象
- 1.4 案例对比分析
- 02.面向对象的思想
- 2.1 面向过程思想
- 2.2 面向过程演变
- 2.3 面向对象思想
- 2.4 面向对象思想特点
- 03.面向对象编程
- 3.1 基础概念
- 3.2 编程语言划分
- 3.3 面向对象分析和设计
- 3.4 UML介绍说明
- 04.面向对象和过程
- 4.1 背景介绍说明
- 4.2 思考几个问题
- 4.3 面向过程编程
- 4.4 面向过程局限性
- 4.5 使用场景分析
- 4.6 案例分析总结
- 4.7 总结和梳理
01.看一个演变案例
1.1 一个案例介绍
面向过程 和面向对象 是两种不同的编程范式。通过一个通俗的案例,可以清晰地展示它们的区别。以下是一个简单的例子:计算矩形的面积和周长。
1.2 普通过程思维
不使用面向对象(面向过程),在面向过程的编程中,代码通常以函数为中心,数据和操作是分离的。
java
public class RectangleWithoutOOP {
public static void main(String[] args) {
// 定义矩形的长和宽
double length = 5.0;
double width = 3.0;
// 计算面积
double area = calculateArea(length, width);
System.out.println("面积: " + area);
// 计算周长
double perimeter = calculatePerimeter(length, width);
System.out.println("周长: " + perimeter);
}
// 计算面积的函数
public static double calculateArea(double length, double width) {
return length * width;
}
// 计算周长的函数
public static double calculatePerimeter(double length, double width) {
return 2 * (length + width);
}
}
特点:
- 数据和操作是分离的。
- 代码逻辑集中在函数中。
- 如果需要对多个矩形进行操作,需要重复定义变量和调用函数。
1.3 使用面向对象
使用面向对象,在面向对象的编程中,代码以对象为中心,数据和操作被封装在类中。
java
// 定义一个矩形类
class Rectangle {
// 属性:长和宽
private double length;
private double width;
// 构造方法:初始化长和宽
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
// 方法:计算面积
public double calculateArea() {
return length * width;
}
// 方法:计算周长
public double calculatePerimeter() {
return 2 * (length + width);
}
}
public class RectangleWithOOP {
public static void main(String[] args) {
// 创建一个矩形对象
Rectangle rectangle = new Rectangle(5.0, 3.0);
// 调用对象的方法计算面积和周长
System.out.println("面积: " + rectangle.calculateArea());
System.out.println("周长: " + rectangle.calculatePerimeter());
}
}
特点:
- 数据和操作被封装在
Rectangle
类中。 - 通过创建对象来操作数据。
- 代码更易于扩展和维护。例如,如果需要添加新的功能(如判断是否为正方形),只需在
Rectangle
类中添加方法即可。
1.4 案例对比分析
对比分析
特性 | 面向过程 | 面向对象 |
---|---|---|
代码组织 | 以函数为中心,数据和操作分离 | 以对象为中心,数据和操作封装在类中 |
可扩展性 | 扩展时需要修改函数或添加新函数 | 扩展时只需添加新的方法或类 |
可维护性 | 代码逻辑分散,维护较困难 | 代码逻辑集中,易于维护 |
复用性 | 复用性较低,需要重复定义变量和调用函数 | 复用性高,通过创建对象即可复用功能 |
适合场景 | 简单、一次性任务 | 复杂、需要长期维护的项目 |
思维习惯 | 面向过程的编程方式更接近计算机的执行方式 | 人类更倾向于以"对象"和"行为"来理解问题,而不是一系列的过程和步骤 |
02.面向对象的思想
2.1 面向过程思想
我们来回想一下,这几天我们完成一个需求的步骤:首先是搞清楚我们要做什么,然后在分析怎么做,最后我们再代码体现。
举个简单的例子:相信大家都被问过这样一个问题: 把大象装入冰箱需要几步?按照面向过程的思想,需要三步:
- 第一步:打开冰箱
- 第二步:把大象塞进去
- 第三步:关上冰箱
一步一步去实现,而具体的每一步都需要我们去实现和操作。这些步骤相互调用和协作,完成我们的需求。
在上面的每一个具体步骤中我们都是参与者,并且需要面对具体的每一个步骤和过程,这就是面向过程最直接的体现。
那么什么是面向过程开发呢? 面向过程开发,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。
2.2 面向过程演变
当需求单一,或者简单时,我们一步一步去操作没问题,并且效率也挺高。
可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦了。这时就开始思索,
能不能把这些步骤和功能在进行封装,封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。
这样结构就清晰了很多。用的时候,找到对应的类就可以了。这就是面向对象的思想。
2.3 面向对象思想
面向对象:就是把现实中的事物都抽象为"对象"。每个对象是唯一的,且都可以拥有它的属性与行为。我们就可以通过调用这些对象的方法、属性去解决问题。
在这个例子中:我们可以把大象看作一个对象,冰箱看作一个对象。冰箱的一些功能:开门、装物体、关门。
面向对象对象的特点:有很好的延展性,比如我给大象赋予了一个吃的功能,它通过调用就可以在冰箱里去吃东西。面向对象就是把现实问题抽象为对象,通过调用每个对象的属性或功能去解决问题。
2.4 面向对象思想特点
- a:是一种更符合我们思想习惯的思想
- b:可以将复杂的事情简单化
- c:将我们从执行者变成了指挥者。角色发生了转换
03.面向对象编程
3.1 基础概念
面向对象编程中有两个非常重要、非常基础的概念,那就是 类(class)和对象(object)。
直到今天,如果不按照严格的定义来说,大部分编程语言都是面向对象编程语言,比如 Java、C++、Go、Python、C#、Ruby、JavaScript、Objective-C、Scala、PHP、Perl 等等。
大部分程序员在开发项目的时候,都是基于面向对象编程语言进行的面向对象编程。
面向对象编程和面向对象编程语言。那究竟什么是面向对象编程?什么语言才算是面向对象编程语言呢?
-
面向对象编程:是一种编程范式或编程风格。它以类或对象作为组织代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石。
-
面向对象编程语言:是支持类或对象的语法机制,并有现成的语法机制,能方便地实现面向对象编程四大特性(封装、抽象、继承、多态)的编程语言。
3.2 编程语言划分
"如果不按照严格的定义来说,大部分编程语言都是面向对象编程语言"。为什么要加上"如果不按照严格的定义"这个前提呢?那是因为,如果按照刚刚我们给出的严格的面向对象编程语言的定义,前面提到的有些编程语言,并不是严格意义上的面向对象编程语言。
比如 JavaScript,它不支持封装和继承特性,按照严格的定义,它不算是面向对象编程语言,但在某种意义上,它又可以算得上是一种面向对象编程语言。为什么这么说呢?
面向对象编程从字面上,按照最简单、最原始的方式来理解,就是将对象或类作为代码组织的基本单元,来进行编程的一种编程范式或者编程风格。并不一定需要封装、抽象、继承、多态这四大特性的支持。
但是,在进行面向对象编程的过程中,人们不停地总结发现,有了这四大特性,我们就能更容易地实现各种面向对象的代码设计思路。
比如,我们在面向对象编程的过程中,经常会遇到 is-a 这种类关系(比如狗是一种动物),而继承这个特性就能很好地支持这种 is-a 的代码设计思路,并且解决代码复用的问题,所以,继承就成了面向对象编程的四大特性之一。
但是随着编程语言的不断迭代、演化,人们发现继承这种特性容易造成层次不清、代码混乱,所以,很多编程语言在设计的时候就开始摒弃继承特性,比如 Go 语言。
3.3 面向对象分析和设计
跟面向对象编程经常放到一块儿来讲的还有另外两个概念,那就是面向对象分析(OOA)和面向对象设计(OOD)。
- 面向对象分析英文缩写是 OOA,全称是 Object Oriented Analysis;
- 面向对象设计的英文缩写是 OOD,全称是 Object Oriented Design。
- OOA、OOD、OOP 三个连在一起就是面向对象分析、设计、编程(实现),正好是面向对象软件开发要经历的三个阶段。
什么是面向对象分析和设计。这两个概念相对来说要简单一些。
面向对象分析与设计中的"分析"和"设计"这两个词,我们完全可以从字面上去理解,不需要过度解读,简单类比软件开发中的需求分析、系统设计即可。
面向对象分析就是要搞清楚做什么,面向对象设计就是要搞清楚怎么做。两个阶段最终的产出是类的设计,包括程序被拆解为哪些类,每个类有哪些属性方法、类与类之间如何交互等等。
不过,你可能会说,那为啥前面还加了个修饰词"面向对象"呢?有什么特殊的意义吗?
在前面加"面向对象"这几个字,是因为我们是围绕着对象或类来做需求分析和设计的。更多博客。
分析和设计两个阶段最终的产出是类的设计,包括程序被拆解为哪些类,每个类有哪些属性方法,类与类之间如何交互等等。它们比其他的分析和设计更加具体、更加落地、更加贴近编码,更能够顺利地过渡到面向对象编程环节。这也是面向对象分析和设计,与其他分析和设计最大的不同点。
面向对象分析、设计、编程到底都负责做哪些工作呢?简单点讲,面向对象分析就是要搞清楚做什么,面向对象设计就是要搞清楚怎么做,面向对象编程就是将分析和设计的的结果翻译成代码的过程。
3.4 UML介绍说明
什么是 UML?我们是否需要 UML?面向对象分析、设计、编程,我们就不得不提到另外一个概念,那就是 UML(Unified Model Language),统一建模语言。
很多讲解面向对象或设计模式的书籍,常用它来画图表达面向对象或设计模式的设计思路。
实际上,UML 是一种非常复杂的东西。它不仅仅包含我们常提到类图,还有用例图、顺序图、活动图、状态图、组件图等。即便仅仅使用类图,学习成本也是很高的。就单说类之间的关系,UML 就定义了很多种,比如泛化、实现、关联、聚合、组合、依赖等。
要想完全掌握,并且熟练运用这些类之间的关系,来画 UML 类图,肯定要花很多的学习精力。而且,UML 作为一种沟通工具,即便你能完全按照 UML 规范来画类图,可对于不熟悉的人来说,看懂的成本也还是很高的。
04.面向对象和过程
4.1 背景介绍说明
实际上,除了面向对象之外,被大家熟知的编程范式还有另外两种,面向过程编程和函数式编程。面向过程这种编程范式随着面向对象的出现,已经慢慢退出了舞台,而函数式编程目前还没有被广泛接受。
很多人搞不清楚面向对象和面向过程的区别,总以为使用面向对象编程语言来做开发,就是在进行面向对象编程了。而实际上,他们只是在用面向对象编程语言,编写面向过程风格的代码而已,并没有发挥面向对象编程的优势。
4.2 思考几个问题
详细对比一下面向过程和面向对象这两种编程范式,带你一块搞清楚下面这几个问题:
- 什么是面向过程编程与面向过程编程语言?
- 面向对象编程相比面向过程编程有哪些优势?
- 为什么说面向对象编程语言比面向过程编程语言更高级?
- 有哪些看似是面向对象实际是面向过程风格的代码?
- 在面向对象编程中,为什么容易写出面向过程风格的代码?
- 面向过程编程和面向过程编程语言就真的无用武之地了吗?
4.3 面向过程编程
面向对象编程语言是支持类或对象的语法机制,并有现成的语法机制,能方便地实现面向对象编程四大特性(封装、抽象、继承、多态)的编程语言。
类比面向对象编程与面向对象编程语言的定义,对于面向过程编程和面向过程编程语言这两个概念,我给出下面这样的定义。
面向过程编程也是一种编程范式或编程风格。它以过程(可以为理解方法、函数、操作)作为组织代码的基本单元,以数据(可以理解为成员变量、属性)与方法相分离为最主要的特点。面向过程风格是一种流程化的编程风格,通过拼接一组顺序执行的方法来操作数据完成一项功能。
面向过程编程语言首先是一种编程语言。它最大的特点是不支持类和对象两个语法概念,不支持丰富的面向对象编程特性(比如继承、多态、封装),仅支持面向过程编程。
4.4 面向过程局限性
这里给出的面向过程编程和面向过程编程语言的定义,也并不是严格的官方定义。之所以要给出这样的定义,只是为了跟面向对象编程及面向对象编程语言做个对比,以方便你理解它们的区别。
用一个例子进一步解释一下。假设我们有一个记录了用户信息的文本文件 users.txt,每行文本的格式是 name&age&gender(比如,小王 & 28 & 男)。我们希望写一个程序,从 users.txt 文件中逐行读取用户信息,然后格式化成 name\tage\tgender(其中,\t 是分隔符)这种文本格式,并且按照 age 从小到达排序之后,重新写入到另一个文本文件 formatted_users.txt 中。
针对这样一个程序的开发,我们一块来看看,用面向过程和面向对象两种编程风格,编写出来的代码有什么不同。
先来看,用面向过程这种编程风格写出来的代码是什么样子的。注意,下面的代码是用 C 语言这种面向过程的编程语言来编写的。
c
struct User {
char name[64];
int age;
char gender[16];
};
struct User parse_to_user(char* text) {
// 将text("小王&28&男")解析成结构体struct User
}
char* format_to_text(struct User user) {
// 将结构体struct User格式化成文本("小王\t28\t男")
}
void sort_users_by_age(struct User users[]) {
// 按照年龄从小到大排序users
}
void format_user_file(char* origin_file_path, char* new_file_path) {
// open files...
struct User users[1024]; // 假设最大1024个用户
int count = 0;
while(1) { // read until the file is empty
struct User user = parse_to_user(line);
users[count++] = user;
}
sort_users_by_age(users);
for (int i = 0; i < count; ++i) {
char* formatted_user_text = format_to_text(users[i]);
// write to new file...
}
// close files...
}
int main(char** args, int argv) {
format_user_file("/home/zheng/user.txt", "/home/zheng/formatted_users.txt");
}
再来看,用面向对象这种编程风格写出来的代码是什么样子的。注意,下面的代码是用 Java 这种面向对象的编程语言来编写的。
java
public class User {
private String name;
private int age;
private String gender;
public User(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public static User praseFrom(String userInfoText) {
// 将text("小王&28&男")解析成类User
}
public String formatToText() {
// 将类User格式化成文本("小王\t28\t男")
}
}
public class UserFileFormatter {
public void format(String userFile, String formattedUserFile) {
// Open files...
List users = new ArrayList<>();
while (1) { // read until file is empty
// read from file into userText...
User user = User.parseFrom(userText);
users.add(user);
}
// sort users by age...
for (int i = 0; i < users.size(); ++i) {
String formattedUserText = user.formatToText();
// write to new file...
}
// close files...
}
}
public class MainApplication {
public static void main(Sring[] args) {
UserFileFormatter userFileFormatter = new UserFileFormatter();
userFileFormatter.format("/home/zheng/users.txt", "/home/zheng/formatted_users.txt");
}
}
从上面的代码中,我们可以看出,面向过程和面向对象最基本的区别就是,代码的组织方式不同。面向过程风格的代码被组织成了一组方法集合及其数据结构(struct User),方法和数据结构的定义是分开的。面向对象风格的代码被组织成一组类,方法和数据结构被绑定一起,定义在类中。
两种编程风格实现的代码貌似差不多啊,顶多就是代码的组织方式有点区别,没有感觉到面向对象编程有什么明显的优势呀!你的感觉没错。之所以有这种感觉,主要原因是这个例子程序比较简单、不够复杂。
4.5 使用场景分析
面向过程的使用场景:整个程序的处理流程只有一条主线,很容易被划分成顺序执行的几个步骤,然后逐句翻译成代码,这就非常适合采用面向过程这种面条式的编程风格来实现。
但对于大规模复杂程序的开发来说,整个程序的处理流程错综复杂,并非只有一条主线。
如果把整个程序的处理流程画出来的话,会是一个网状结构。如果我们再用面向过程编程这种流程化、线性的思维方式,去翻译这个网状结构,去思考如何把程序拆解为一组顺序执行的方法,就会比较吃力。这个时候,面向对象的编程风格的优势就比较明显了。
4.6 案例分析总结
面向对象编程是以类为思考对象。在进行面向对象编程的时候,我们并不是一上来就去思考,如何将复杂的流程拆解为一个一个方法,而是采用曲线救国的策略,先去思考如何给业务建模,如何将需求翻译为类,如何给类之间建立交互关系,而完成这些工作完全不需要考虑错综复杂的处理流程。
当我们有了类的设计之后,然后再像搭积木一样,按照处理流程,将类组装起来形成整个程序。这种开发模式、思考问题的方式,能让我们在应对复杂程序开发的时候,思路更加清晰。
除此之外,面向对象编程还提供了一种更加清晰的、更加模块化的代码组织方式。比如,我们开发一个电商交易系统,业务逻辑复杂,代码量很大,可能要定义数百个函数、数百个数据结构,那如何分门别类地组织这些函数和数据结构,才能不至于看起来比较凌乱呢?类就是一种非常好的组织这些函数和数据结构的方式,是一种将代码模块化的有效手段。
你可能会说,像 C 语言这种面向过程的编程语言,我们也可以按照功能的不同,把函数和数据结构放到不同的文件里,以达到给函数和数据结构分类的目的,照样可以实现代码的模块化。你说得没错。只不过面向对象编程本身提供了类的概念,强制你做这件事情,而面向过程编程并不强求。这也算是面向对象编程相对于面向过程编程的一个微创新吧。
实际上,利用面向过程的编程语言照样可以写出面向对象风格的代码,只不过可能会比用面向对象编程语言来写面向对象风格的代码,付出的代价要高一些。而且,面向过程编程和面向对象编程并非完全对立的。很多软件开发中,尽管利用的是面向过程的编程语言,也都有借鉴面向对象编程的一些优点。
4.7 总结和梳理
一起总结回顾一下,你需要重点掌握的几个概念和知识点。
1.什么是面向对象编程?面向对象编程是一种编程范式或编程风格。它以类或对象作为组织代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石 。
2.什么是面向对象编程语言?面向对象编程语言是支持类或对象的语法机制,并有现成的语法机制,能方便地实现面向对象编程四大特性(封装、抽象、继承、多态)的编程语言。
3.如何判定一个编程语言是否是面向对象编程语言?如果按照严格的的定义,需要有现成的语法支持类、对象、四大特性才能叫作面向对象编程语言。如果放宽要求的话,只要某种编程语言支持类、对象语法机制,那基本上就可以说这种编程语言是面向对象编程语言了,不一定非得要求具有所有的四大特性。
4.面向对象编程和面向对象编程语言之间有何关系?面向对象编程一般使用面向对象编程语言来进行,但是,不用面向对象编程语言,我们照样可以进行面向对象编程。反过来讲,即便我们使用面向对象编程语言,写出来的代码也不一定是面向对象编程风格的,也有可能是面向过程编程风格的。
5.什么是面向对象分析和面向对象设计?简单点讲,面向对象分析就是要搞清楚做什么,面向对象设计就是要搞清楚怎么做。两个阶段最终的产出是类的设计,包括程序被拆解为哪些类,每个类有哪些属性方法、类与类之间如何交互等等。
面向对象编程相比面向过程编程有哪些优势?面向对象编程相比起面向过程编程的优势主要有三个。
- 对于大规模复杂程序的开发,程序的处理流程并非单一的一条主线,而是错综复杂的网状结构。面向对象编程比起面向过程编程,更能应对这种复杂类型的程序开发。
- 面向对象编程相比面向过程编程,具有更加丰富的特性(封装、抽象、继承、多态)。利用这些特性编写出来的代码,更加易扩展、易复用、易维护。
- 从编程语言跟机器打交道的方式的演进规律中,我们可以总结出:面向对象编程语言比起面向过程编程语言,更加人性化、更加高级、更加智能。
05.更多内容推荐
模块 | 描述 | 备注 |
---|---|---|
GitHub | 多个YC系列开源项目,包含Android组件库,以及多个案例 | GitHub |
博客汇总 | 汇聚Java,Android,C/C++,网络协议,算法,编程总结等 | YCBlogs |
设计模式 | 六大设计原则,23种设计模式,设计模式案例,面向对象思想 | 设计模式 |
Java进阶 | 数据设计和原理,面向对象核心思想,IO,异常,线程和并发,JVM | Java高级 |
网络协议 | 网络实际案例,网络原理和分层,Https,网络请求,故障排查 | 网络协议 |
计算机原理 | 计算机组成结构,框架,存储器,CPU设计,内存设计,指令编程原理,异常处理机制,IO操作和原理 | 计算机基础 |
学习C编程 | C语言入门级别系统全面的学习教程,学习三到四个综合案例 | C编程 |
C++编程 | C++语言入门级别系统全面的教学教程,并发编程,核心原理 | C++编程 |
算法实践 | 专栏,数组,链表,栈,队列,树,哈希,递归,查找,排序等 | Leetcode |
Android | 基础入门,开源库解读,性能优化,Framework,方案设计 | Android |
23种设计模式
23种设计模式 & 描述 & 核心作用 | 包括 |
---|---|
创建型模式 提供创建对象用例。能够将软件模块中对象的创建和对象的使用分离 | 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
结构型模式 关注类和对象的组合。描述如何将类或者对象结合在一起形成更大的结构 | 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
行为型模式 特别关注对象之间的通信。主要解决的就是"类或对象之间的交互"问题 | 责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) |