1.引子
任何事物的发展,都遵循了一定的定律。比如说计算机,从最初的大型机,小型机、到PC机,再到今天的各种智能终端设备,越来越普及,越来越懂你我;比如说网络,从最初的单机,到局域网,到因特网,移动互联网,再到今天的IoT万物互联。
我们编程也一样,从最初的面向二进制编程,到汇编,到面向过程编程,再到今天的主流:面向对象编程,和函数式编程。在这其中,你可能已经发现了,发展的规律都是在向人性化的方向发展。
关于人性化,你可以这么去理解。一个是沟通语言上,要越来越要求讲人话,用人类的语言来沟通;一个是思维上,更加符合人类的思维模型。这也是编程语言从低级,向高级编程语言进化的方向。
我们具体来看一下。
二进制、汇编编程时代,你需要懂机器,需要告诉机器0是向左,1是向右,就是要通过指令来驱动机器的方式。这是专家编程的年代,普通人想都别想,所以你不难想象生产力是相当低下的;
面向过程编程时代,我们不需要像二进制时代那么辛苦了,你不需要太关心cpu、内存、磁盘,可以通过你日常习惯的语言来表达任务,通过编译器告诉机器,它需要怎么做即可。这里的编译器就相当于我们生活中的翻译,有了它不仅降低了我们编程的难度,同时有效提升了我们的编程效率;
面向对象编程时代,以及函数式编程(函数式编程,把函数作为方法参数进行传递,可以看成是面向对象编程的一种有效补充),则更进了一步。不仅语言表达方式上是你喜欢舒服的方式,就连思考想问题的方式都是我们熟悉的配方了。简单说就是语言表达、思维模型都与我们一致了。所以这个时候,才有人敢跟你说,多少天从0精通什么什么的对吧,虽然有点扯,但至少说明了面向对象编程时代,编程的难度与门槛都非常低了,一步就能跨过去。不信你看你一不懂操作系统,二不懂数据结构算法,三还不懂网络 ,你一样把CRUD写的很好(ˆ▽ˆ)
但是作为程序员,我们应该要做一个有追求的程序员。千万不要信市面上那些xx天精通xxx的鬼,百害而无一利!我们更加应该沉下心来,拨开表象直击本质,这也是这个系列我想要通过抛砖引玉,分享给你的内功心法。
那么让我们开始吧。
2.面向过程、面向对象、函数式编程
接下来我想跟你聊一聊编程范式,这也是我们这一篇分享的重点。如果你是入行不久经验尚浅,你可能会问:什么是编程范式?你姑且把它理解为编程风格,比如说面向过程编程风格,面向对象编程风格,函数式编程风格。
考虑到大多数新生代程序员朋友,更熟悉面向对象编程一些。我们先来看面向对象编程范式。这里我想先给你抛出一个场景,假设你正在寻找新的工作机会,对面正坐着面试官,他想要跟你聊一聊关于面向对象编程。你准备怎么跟他聊呢?
你可能会说,面向对象编程它有类和对象,还拥有封装、抽象、继承、多态四大特性。很精简,然后没了!
当然如果我是你对面的面试官,这样的回答,我肯定是不满意的。我可能会认为你是把封装、抽象、继承、多态背下来了,但是你并不理解它!
这里,我们不单要知道每一种编程范式它有什么特性,且每一种特性存在的意义,解决了什么问题。比如说关于什么是面向对象编程?我们可以这么去思考:
- 面向对象编程,它是一种编程范式,编程风格
- 面向对象编程,它是通过类和对象作为组织代码的基本单元。想想我们日常开发中,你是不是首先要定义类class,然后创建该类的对象object,并使用它。所以我们说面向对象编程,它是以类和对象作为组织代码的基本单元
- 面向对象编程,它将数据表达为类的成员变量,将操作表达为类的方法,通过封装将数据与操作绑定在一起。有特定的语言机制支持封装、抽象、继承、多态四大特性,至于四大特性的详细内容,我们在下一小结分享
- 面向对象编程语言代表:java
知道了什么是面向对象编程范式。我们再来看面向过程编程,那什么是面向过程编程呢?我们可以这么去思考:
- 面向过程编程,它是早于面向对象编程出现的一种编程范式,编程风格
- 面向过程编程,它是通过过程(这里过程可以叫做函数、或者方法、或者操作)作为组织代码的基本单元。它没有类,也没有对象
- 面向过程编程,它将数据表达为结构体(struct),将操作表达为方法,没有封装特性,它的数据与操作是分开维护的
- 面向过程编程语言代表:c
最后我们再来看函数式编程:
- 函数式编程,它是一种编程范式,编程风格
- 函数式编程,它把函数作为一等公民,即它的函数可以像面向对象编程中的对象一样使用,比如说作为参数进行传递
- 函数式编程,最大的优势是通过函数式编程,可以让我们编写的代码更加简洁,更加优雅。想想你熟悉的jdk8开始支持的lambda表达式,是不是你很享受lambda表达式带来的简洁代码
- 函数式编程编程语言代表:scala、go
三种编程范式,面向过程编程范式用的越来越少了。原因是我们今天的软件,应用系统复杂度越来越高,而面向过程编程范式,它适合于编写流程化、流水线面条式的简单软件系统,具体说就是第1步做什么,第2步做什么......第n步做什么,流水线式的编程。
而复杂软件系统,不是简单的第1步,第2步......第n步流水线式的编程模型,它拥有错综复杂的关系,不是一条流水线,它是复杂的网状编程模型。我们需要面向对象这种编程范式来应对。
因此面向对象编程范式,它是我们今天主流的编程范式。函数式编程范式虽然表现抢眼,但是她限于一些特定的应用场景,比如说算法编码,因此从我个人的角度,我理解函数式编程范式,可以看成是面向对象编程范式的有效补充
3.面向对象编程
现在我已经给你分享了面向对象编程范式,是我们今天的主流编程范式。那么这一小结我想进一步给你分享面向对象编程的四大特性,以及在日常开发中我们应该如何通过面向对象分析、面向对象设计、面向对象编程完成软件系统。
3.1.面向对象编程四大特性
我们知道面向对象编程范式,它是以类和对象作为组织代码的基本单元,并有特定的语法机制支持封装、抽象、继承、多态四大特性。接下来我们一起来看一看,每一种特性存在的意义,以及解决的问题。
- 封装
/*
*1.什么是封装?
*/
封装是实现信息隐藏,或者叫做数据访问保护的特性。想想你的项目中,这里假定你们的项目是通过java编程语言开发,你会怎么做呢?下面是一个示例:
public class Order{
private String orderId;
private String userId;
......
/***********get/set方法***************/
public void setOrderId(String orderId){...}
public String getOrderId(){...}
public void setUserId(String orderId){...}
public String getUserId(){...}
}
我们通过private 实现了成员变量orderId,userId的信息隐藏,即你不能直接访问成员变量,只能通过暴露的特定操作接口(get/set方法)进行访问。你看这就是封装
/*
*2.支持封装特性的语法机制?
*/
封装特性需要特定的语法机制来支持,在java编程语言中,通过访问控制修饰符语法机制来支持封装,具体有:private protected,public等
/*
*3.封装特性解决了什么问题?
*/
通过封装特性,我们实现了信息隐藏(数据访问保护)。即你不能直接访问操作类的成员变量,只能通过暴露的特定接口访问。这样带来的好处是易用性(使用者只需要关注暴露的接口,不需要关心类的细节);可维护性(不能直接访问操作类的成员变量,只能通过暴露的接口操作访问,实现类信息的统一,一致维护)
- 抽象
/*
*1.什么是抽象?
*/
抽象也是实现隐藏,与封装实现信息隐藏不同,抽象是实现操作具体实现的隐藏。你可以简单理解为抽象是方法实现细节的隐藏。下面是一个示例:
public interface Logger{
void printLog();
}
public class MyLogger implements Logger{
@override
public void pringLog(){
......
}
}
我们通过Logger接口,暴露打印日志的方法printLog,你需要注意接口中该方法并未实现,对于使用者来说,并不需要关注实现细节,我们只需要关心接口方法能够提供什么能力,具体怎么提供的不需要关心。你看这就是抽象
/*
*2.支持抽象特性的语法机制?
*/
抽象是面向对象编程的一大特性,你需要注意它不是面向对象独有的特性。这也是为什么很多时候,我们都说面向对象的三大特性(三大独有特性)封装、继承、多态的原因。在java编程语言中,支持抽象的语法机制有:接口、抽象类,甚至普通方法也支持抽象特性
/*
*3.抽象特性解决了什么问题?
*/
通过抽象特性,我们实现了操作方法实现细节的隐藏。这样带来的好处是易用性(使用者只需要关注操作方法提供了什么能力,并不需要关心它是怎么提供的)
- 继承
/*
*1.什么是继承?
*/
继承表达的是is-a关系,比如说猫狗是一种动物。继承比较好理解,儿子继承父亲对吧。下面是一个示例:
public class Parent{
}
public class Child extends Parent{
}
/*
*2.支持继承特性的语法机制
*/
在java语言中,支持继承的语法机制是extend关键字。
/*
*3.继承特性解决了什么问题?
*/
通过继承特性,表达is-a关系,猫狗是一种动物,儿子继承父亲。这样带来的好处是复用性(父亲有的,儿子拿过来用就是了,不管有多少个儿子,都可以直接用父亲的,实现复用)
但是你需要注意,在实际应用中,我们不推荐使用继承,比如说优先推荐使用组合,甚至在go语言中,直接不支持了继承的特性。
这是为什么呢?这是因为继承带来复用性的同时,增加了代码的复杂性,以及降低了代码可读性
- 多态
/*
*1.什么是多态?
*/
多态表达的是子类可以替换父类,父类出现的地方,儿子都可以出现,并且运行时执行的子类的代码。这是很有用的特性,多态是很多设计原则,设计模式实现的基石。下面是一个示例:
public interface Logger{
void writeLog();
}
public class FileLogger implements Logger{
@override
public void writeLog(){
// 将日志写到文件
}
}
public class DbLogger implements Logger{
@override
public void writeLog(){
// 将日志写到数据库
}
}
public class 上帝类{
public static void main(String[] args){
Logger logger = new FileLogger();
logger.writeLog();//将日志写到文件
}
}
通过多态特性,实现了运行时日志的切换(选择将日志写到文件,或者写到数据库)。你看这就是多态。
/*
*2.支持多态特性的语法机制
*/
在java编程语言中,支持多态的语法机制有:implements、extends、方法重写、反射
/*
*3.多态特性解决了什么问题?
*/
通过多态特性,实现了运行时切换日志(将日志写入文件,或者将日志写入数据库)。这样带来的好处是扩展性(如果要支持新的日志写入方式,我们只需要增加一个新的实现类,实现Logger接口即可,原有的代码不需要修改)。你看这就是多态带来的好处,扩展性。
3.2.面向对象分析、设计、编程
现在你已经知道了面向对象编程的四大特性:封装、抽象、继承和多态,知道了它们存在的意义,解决的问题,以及特定的语法机制。接下来我顺便再简单的给你分享,在实际项目开发中,我们如何进行面向对象分析(OOA)、面向对象设计(OOD)、面向对象编程(OOP)。
面向对象分析,简单理解是解决要做什么的问题(需求分析),面向对象设计解决的的要怎么做的问题(有哪些类,类的属性和方法,类之间的交互关系),面向对象编程是把分析与设计通过代码表达出来。具体点,我们说一说人话:
- 根据需求分析,进行业务建模,找出有哪些类
- 进一步梳理出类有哪些成员变量,类需要暴露哪些操作方法
- 进一步梳理出类之间的交互关系,你如何调用我,我如何引用你
- 进一步通过上帝类(执行入口,比如main方法),组装成一个成品系统
好了,今天的内容有点长,我们暂时告一段落了,期望这篇文章能为你理解面向对象编程范式,带来一些帮助(ˆ▽ˆ) 。