引言
面向对象(Object-Oriented Programming, OOP)和面向过程(Procedural Programming)是两种不同的编程范式。不能因为我们接下来要进入“面向对象”的模块的学习,就武断地说,面向对象一定比面向过程好。两者都有各自的适用场景,只能说哪种场景更适合哪种范式而已。
本文将对面向对象的核心理念,面向对象的基本概念做一些介绍,为后续面向对象的学习打下一个观念上的基础。
为什么需要面向对象
就日常的数据处理任务来说,面向过程的范式,更加适合,而且把任务按照处理的流程,分步骤实现,每个步骤单独定义对应的处理函数,更加清晰且易于理解。尤其在涉及到算法实现时,面向过程编程更可以清晰地表达算法的步骤和逻辑。
然而,当任务比较复杂时,我们会发现,通过面向过程的范式进行编程时,需要使用大量的全局变量和函数间的参数和返回值进行交互中间数据的传递,整个代码表达上会变得很繁琐,反而使程序不那么清晰了。这时候,面向对象的编程范式就更加适用了。
此外,从更本质上来看,两种编程范式进行上推,其实是所在系统分析与设计的范式的不同。面向过程的需求分析、系统设计,是以数据、流程为核心的。而面向对象是以实体的抽象为核心的。
面向对象的核心理念
只要稍微了解过面向对象,大概都会清楚,面向对象有三大核心理念,也就是:封装、继承和多态。
1、封装
首先是封装,在面向过程范式中,数据表达和数据处理是相分离的,数据表达一般是通过全局变量的形式实现,数据的处理是通过自定义函数的形式实现。处理逻辑如果很复杂的话,全局变量和函数之间的相互关联关系将变得不那么直观,代码的可读性也变得越来越差。
而在面向对象中,数据的表达和数据的处理被放到了一个叫做对象的实体中。数据作为对象的属性存储,数据的处理作为对象中的方法来实现。同一个对象的所有方法可以随时访问对象中的属性,而不需要通过参数传递的方式来进行。
此外,相较于全局变量的方式进行数据的共享,面向对象的封装,通过属性进行数据的共享,变得相对安全一些,不必但系全局变量被其他函数误操作了(随着,这种安全性也更多是一种设计理念上的约定的存在,而非绝对的安全)。
2、继承
面向过程的编程中,功能的复用,可以通过函数的调用实现,这种复用关系,其实是相对松散一些的。复用的形式比较直接、易于掌握。
而面向对象中,通过继承,子类可以自动复用父类中的属性和方法,复用关系比面向过程中更加紧密,但又更加自然。
3、多态
多态的理念,更多的其实是可扩展性的体现。面线过程中,要实现可扩展,要改动的地方会比较多,一般通过增加分支判断,来进行扩展功能的引入。
而面向对象中,变得相对简单。扩展功能的引入,通过定义新的子类或者新的实现特定接口协议的类,在调用方的代码无需做任何修改。
此外,相较于静态类型的编程语言,Python这种动态类型的语言的多态实现,变得更加灵活、自然。
面向对象的基本概念
除了前面提到的面向对象的三大核心理念外,面向对象中还有几个基本概念,需要提前在脑子里留个印象。
1、类(Class)
类可以理解为是对象的模板、蓝图,类中定义了对象需要的属性和方法。
2、对象(Object)
对象这个概念,任何一个学习、使用Python的人,都可能是“日用而不知”的。对象是类的实例,具有类中定义的属性和方法,是数据的表达和数据的处理的封装实体。
3、属性(Attribute)
对象中的属性,类似于变量的存在,用以存储对象实例的状态。
4、方法(Method)
类中定义的数据处理的方法,定义了对象应该具有的行为。
面向对象的举例
我们以一个产研团队进行产品的设计研发为例,简单对比一下面向过程与面向对象的使用上的区别。
1、小的研发团队,只有三个人:产品经理、前端开发、后端开发
面向过程的团队工作:
# 假如我们现在有一个三人团队:产品经理、前端开发、后端开发
# 面向过程的产品研发
team = [{'name': '狗子', 'duty': 'PM'},{'name': '小花', 'duty': 'FRD'},{'name': '光头', 'duty': 'BRD'},
]def product_design(name):print(f"产品经理【{name}】进行产品设计")def frontend_coding(name):print(f"前端开发【{name}】进行前端开发")def backend_coding(name):print(f"后端开发【{name}】进行后端开发")def team_work(workers):for dgr in workers:if dgr['duty'] == 'PM':product_design(dgr['name'])continueif dgr['duty'] == 'FRD':frontend_coding(dgr['name'])continueif dgr['duty'] == 'BRD':backend_coding(dgr['name'])continueelse:print('你是哪里冒出来的')if __name__ == '__main__':team_work(team)
执行结果:
面向对象的团队工作:
# 假如我们现在有一个三人团队:产品经理、前端开发、后端开发
# 面向对象的产品研发class DaGongRen(object):def __init__(self, name):self.name = namedef work(self):passclass ProductManager(DaGongRen):def work(self):print(f"产品经理【{self.name}】进行产品设计")class FrontendProgrammer(DaGongRen):def work(self):print(f"前端开发【{self.name}】进行前端开发")class BackendProgrammer(DaGongRen):def work(self):print(f"后端开发【{self.name}】进行后端开发")team = [ProductManager('狗子'), FrontendProgrammer('小花'), BackendProgrammer('光头')]def team_work(workers):for dgr in workers:dgr.work()if __name__ == '__main__':team_work(team)
执行结果:
2、现在团队经费足够了,可以夸大团队规模了,我们增加了测试工程师
面向过程的代码有一处新增、两处修改:
执行结果:
面向对象的代码有一处新增、一处修改:
执行结果:
通过前面的代码示例,应该能够稍微理解面向对象与面向过程的异同。
此外,关于代码的可扩展性,也能理解何为面向对象扩展功能时,调用方不需要修改代码的好处了。
如何学习面向对象
对于新手来说,面向对象是一种新的编程范式、新的设计理念。
学习面向对象的核心在于对这种新的设计理念的三个核心理念的理解、消化。而不是对相关的语法的填鸭式记忆。
实际编程实践中,没有必要为了面向对象而面向对象。
可以试着从数据的表达和数据的处理的封装特性作为体悟面向对象这种思想的一个落脚点,然后不断实践,从实践中加深对面向对象的理解。