面向对象与面向过程

面向对象怎么理解

面向对象编程和面向对象编程语言

面向对象编程最重要、基础的概念就是类和对象,今天我们用到的很多语言像Java、C++、Go、Pyhton、JavaScript等都是。那究竟什么是面向对象编程?什么是面向对象编程语言呢?

  1. 面向对象编程是一种编程范式或编程风格。它以类或对象为组织代码的基本单元,并将封装、抽象、继承、多态四个特性作为代码设计和实现的基石。
  2. 面向对象编程语言是支持类或对象的语法机制,可以方便的实现面向对象编程的四大特性的编程语言。

一般来讲,面向对象编程都是通过使用面向对象编程语言来进行的,但是,不用面向对象编程语言也照样可以进行面向对象编程。反之,即使使用面向对象编程语言,写出来的代码也不一定是面向对象编程风格的。
总之,如何判断某编程语言是面向对象还是面向过程的,只要它支持类或对象的语法概念,并以此作为组织代码的基本单元,那就可以认为是面向对象的编程语言。至于是否完全支持四大特性,并不作为判定标准。

面向对象的四大特性

封装

  • 定义:类通过暴露有限的访问接口,授权外部仅能通过类提供的方式(或者叫函数)来访问内部信息或者数据。(对于封装这个特性,我们需要编程语言本身提供一定的语法机制来支持。这个语法机制就是访问权限控制。例如 private、public 等关键字就是 Java 语言中的访问权限控制语法)
  • 意义:如果我们对类中属性的访问不做限制,那任何代码都可以访问、修改类中的属性,虽然这样看起来更加灵活,但从另一方面来说,过度灵活也意味着不可控,属性可以随意被以各种奇葩的方式修改,而且修改逻辑可能散落在代码中的各个角落,势必影响代码的可读性、可维护性。另外,类仅仅通过有限的方法暴露必要的操作,也能提高类的易用性。如果我们把类属性都暴露给类的调用者,调用者想要正确地操作这些属性,就势必要对业务细节有足够的了解。而这对于调用者来说也是一种负担。相反,如果我们将属性封装起来,暴露少许的几个必要的方法给调用者使用,调用者就不需要了解太多背后的业务细节,用错的概率就减少很多。

继承

  • 定义:继承是用来表示类之间的 is-a 关系,编程语言需要提供特殊的语法机制来支持,比如Java 使用 extends 关键字来实现继承,C++ 使用冒号(class B : public A)。注:java不支持多继承的原因:多重继承有副作用—钻石问题(菱形继承,假设类 B 和类 C 继承自类 A,且都重写了类 A 中的同一个方法,而类 D 同时继承了类 B 和类 C,那么此时类 D 会继承 B、C 的方法,那对于 B、C 重写的 A 中的方法,类 D 会继承哪一个呢?这里就会产生歧义)。但是 Java 支持多接口实现,因为接口中的方法,是抽象的,就算一个类实现了多个接口,且这些接口中存在某个同名方法,但是我们在实现接口的时候,这个同名方法需要由我们这个实现类自己来实现,所以并不会出现二义性的问题。
  • 意义:继承最大的一个好处就是代码复用。(子类继承父类就可以重用父类的代码,不过过度使用继承,继承层次过深过复杂,就会导致代码可读性、可维护性变差。注:里氏替换原则去考察继承是否合理

抽象

  • 定义:抽象讲的是如何隐藏方法的具体实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的。(常借助编程语言提供的接口类(比如 Java 中的 interface 关键字语法)或者抽象类(比如 Java 中的 abstract 关键字语法)这两种语法机制,来实现抽象这一特性。但实际上,并不是说一定要为实现类抽象出接口类,才叫作抽象。即便不编写接口类,单纯的实现类本身就满足抽象特性。之所以这么说,那是因为,类的方法是通过编程语言中的“函数”这一语法机制来实现的。**通过函数包裹具体的实现逻辑,这本身就是一种抽象。**调用者在使用函数的时候,并不需要去研究函数内部的实现逻辑,只需要通过函数的命名、注释或者文档,了解其提供了什么功能,就可以直接使用了。)
  • 意义:抽象作为一种只关注功能点不关注实现的设计思路在代码设计中,起到非常重要的指导作用。比如基于接口而非实现编程、开闭原则(对扩展开放、对修改关闭)、代码解耦(降低代码的耦合性)等。

多态

  • 定义:多态是指,子类可以替换父类。同一个操作表现出不同的行为就叫多态。

  • 实现多态的两种方法:
    1、继承加方法重写:
    1)首先编程语言要支持父类对象可以引用子类对象
    2)其次编程语言要支持继承(有了继承才可以将子类对象传递给父类)
    3)编程语言要支持子类可以重写父类中的方法
    2、利用接口类语法实现多态:比如Java中同一个接口有不同的实现类

  • 意义:提高代码的可扩展性和复用性。利用多态特性,可以将不同的类型传递给同一个函数,一个函数即可实现不同类型的同一个操作,而不需要为每个不同类型的类都创建一个函数,提高了代码的复用性。

面向对象相比面向过程的优势

面向过程的概念

类比面向对象给出面向过程的概念。

  1. 面向过程编程也是一种编程范式或编程风格。它以过程(可以理解为方法、函数、操作)作为组织代码的基本单元,以数据(可以理解为成员变量、属性)与方法相隔离为最主要的特点。面向过程是一种流程化的编程风格,通过拼接一组顺序执行的方法来操作数据完成一项功能。
  2. 面向过程编程语言最大的特点是不支持类和对象两个语法概念,不支持丰富的面向对象编程特性。

面向对象与面向过程的区别

1、**面向过程和面向对象最基本的区别就是代码的组织方式不同。**面向过程风格的代码被组织成了一组方法集合及其数据结构,方法与数据像分离。面向对象风格的代码被组织成一组类,方法和数据绑在一起,定义在类中。
2、**面向对象更加能够应对大规模复杂程序的开发。**于大规模复杂程序的开发来说,整个程序的处理流程错综复杂,并非只有一条主线。如果把整个程序的处理流程画出来的话,会是一个网状结构。如果我们再用面向过程编程这种流程化、线性的思维方式,去翻译这个网状结构,去思考如何把程序拆解为一组顺序执行的方法,就会比较吃力。这个时候,面向对象的编程风格的优势就比较明显了。面向对象编程是以类为思考对象。在进行面向对象编程的时候,我们并不是一上来就去思考,如何将复杂的流程拆解为一个一个方法,而是采用曲线救国的策略,先去思考如何给业务建模,如何将需求翻译为类,如何给类之间建立交互关系,而完成这些工作完全不需要考虑错综复杂的处理流程。当我们有了类的设计之后,然后再像搭积木一样,按照处理流程,将类组装起来形成整个程序。这种开发模式、思考问题的方式,能让我们在应对复杂程序开发的时候,思路更加清晰。除此之外,面向对象编程还提供了一种更加清晰的、更加模块化的代码组织方式。比如,我们开发一个电商交易系统,业务逻辑复杂,代码量很大,可能要定义数百个函数、数百个数据结构,那如何分门别类地组织这些函数和数据结构,才能不至于看起来比较凌乱呢?类就是一种非常好的组织这些函数和数据结构的方式,是一种将代码模块化的有效手段。
3、**面向对象风格的代码更易复用、易扩展、易维护。**面向对象编程提供的封装、抽象、继承、多态这些特性,能极大地满足复杂的编程需求,能方便我们写出更易复用、易扩展、易维护的代码。
4、从编程语言跟机器打交道的方式的演进规律中,我们可以总结出:面向对象编程语言比起面向过程编程语言,更加人性化、更加高级、更加智能。

哪些代码设计看似是面向对象实际是面向过程风格

  1. 滥用setter、getter方法。
    项目开发中经常定义完类的属性之后,就顺手把这些属性的 getter、setter 方法都定义上。有些更加省事,直接用 IDE 或者 Lombok 插件(如果是 Java 项目的话)自动生成所有属性的 getter、setter 方法。
    实际上,它违反了面向对象编程的封装特性,相当于将面向对象编程风格退化成了面向过程编程风格。
    在设计实现类的时候,除非真的需要,否则,尽量不要给属性定义 setter 方法。除此之外,尽管 getter 方法相对 setter 方法要安全些,但是如果返回的是集合容器,也要防范集合内部数据被修改的危险。
    2、滥用全局变量和全局方法
    3、定义方法和数据分离的类
    业务开发中很多都是基于MVC三层架构进行开发的,这已经成为一种标准的web开发模式,但它实际上是违背了面向对象的编程风格,准确来讲是一种基于贫血模式的MVC三层架构开发模式。
    MVC 三层架构中的 M 表示 Model,V 表示 View,C 表示 Controller。它将整个项目分为三层:展示层、逻辑层、数据层。MVC 三层开发架构是一个比较笼统的分层方式,落实到具体的开发层面,很多项目也并不会 100% 遵从 MVC 固定的分层方式,而是会根据具体的项目需求,做适当的调整。比如,现在很多 Web 或者 App 项目都是前后端分离的,后端负责暴露接口给前端调用。这种情况下,我们一般就将后端项目分为 Repository 层、Service 层、Controller 层。其中,Repository 层负责数据访问,Service 层负责业务逻辑,Controller 层负责暴露接口。
    我们平时开发 Web 后端项目的时候,基本上都是这么组织代码的。其中,UserEntity 和 UserRepository 组成了数据访问层,UserBo 和 UserService 组成了业务逻辑层,UserVo 和 UserController 在这里属于接口层。从代码中,我们可以发现,UserBo 是一个纯粹的数据结构,只包含数据,不包含任何业务逻辑。业务逻辑集中在 UserService 中。我们通过 UserService 来操作 UserBo。换句话说,Service 层的数据和业务逻辑,被分割为 BO 和 Service 两个类中。像 UserBo 这样,只包含数据,不包含业务逻辑的类,就叫作贫血模型(Anemic Domain Model)。同理,UserEntity、UserVo 都是基于贫血模型设计的。这种贫血模型将数据与操作分离,破坏了面向对象的封装特性,是一种典型的面向过程的编程风格。
    对应的另一种最近更加被推崇的开发模式:基于充血模型的 DDD 开发模式。
    在贫血模型中,数据和业务逻辑被分割到不同的类中。**充血模型(Rich Domain Model)**正好相反,数据和对应的业务逻辑被封装到同一个类中。因此,这种充血模型满足面向对象的封装特性,是典型的面向对象编程风格。
    领域驱动设计,即 DDD,主要是用来指导如何解耦业务系统,划分业务模块,定义业务领域模型及其交互。领域驱动设计这个概念并不新颖,早在 2004 年就被提出了,到现在已经有十几年的历史了。不过,它被大众熟知,还是基于另一个概念的兴起,那就是微服务。
    基于充血模型的 DDD 开发模式实现的代码,也是按照 MVC 三层架构分层的。Controller 层还是负责暴露接口,Repository 层还是负责数据存取,Service 层负责核心业务逻辑。它跟基于贫血模型的传统开发模式的区别主要在 Service 层。
    在基于贫血模型的传统开发模式中,Service 层包含 Service 类和 BO 类两部分,BO 是贫血模型,只包含数据,不包含具体的业务逻辑。业务逻辑集中在 Service 类中。在基于充血模型的 DDD 开发模式中,Service 层包含 Service 类和 Domain 类两部分。Domain 就相当于贫血模型中的 BO。不过,Domain 与 BO 的区别在于它是基于充血模型开发的,既包含数据,也包含业务逻辑。而 Service 类变得非常单薄。总结一下的话就是,基于贫血模型的传统的开发模式,重 Service 轻 BO;基于充血模型的 DDD 开发模式,轻 Service 重 Domain。
    既然基于贫血模型的这种传统开发模式是面向过程编程风格的,那它又为什么会被广大程序员所接受呢?关于这个问题,总结了下面三点原因。
    1、大部分情况下,我们开发的系统业务可能都比较简单,简单到就是基于 SQL 的 CRUD 操作,所以,我们根本不需要动脑子精心设计充血模型,贫血模型就足以应付这种简单业务的开发工作。
    2、充血模型的设计要比贫血模型更加有难度。因为充血模型是一种面向对象的编程风格。我们从一开始就要设计好针对数据要暴露哪些操作,定义哪些业务逻辑。而不是像贫血模型那样,我们只需要定义数据,之后有什么功能开发需求,我们就在 Service 层定义什么操作,不需要事先做太多设计。
    3、思维已固化,转型有成本。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值