【小家java】POP(面向过程编程)、OOP(面向对象编程)、AOP(面向切面编程)三种编程思想的区别和联系

版权声明: https://blog.csdn.net/f641385712/article/details/83507404

相关阅读

【小家java】java5新特性(简述十大新特性) 重要一跃
【小家java】java6新特性(简述十大新特性) 鸡肋升级
【小家java】java7新特性(简述八大新特性) 不温不火
【小家java】java8新特性(简述十大新特性) 饱受赞誉
【小家java】java9新特性(简述十大新特性) 褒贬不一
【小家java】java10新特性(简述十大新特性) 小步迭代
【小家java】java11新特性(简述八大新特性) 首个重磅LTS版本


前言

现在很多语言都是面向对象(OOP)的编程思想,比如Java、Python、JS等为代表。然后聊起面向对象,虽然我们一直在用,但真的问起你什么叫面向对象时,还是很难有一个具相的回答,因此本文尝试以OOP为中心,以讲解和对比的方式,聊聊这三种编程思想。

OOP前夕:POP

谈起了OOP,我们就不得不了解一下POP即面向过程程序设计,它是以功能为中心来进行思考和组织的一种编程方式,强调的是系统的数据被加工和处理的过程,说白了就是注重功能性的实现,效果达到就好了,而OOP则注重封装,强调整体性的概念,以对象为中心,将对象的内部组织与外部环境区分开来。之前看到过一个很贴切的解释,博主把它们画成一幅图如下:
POP和OOP的对比图
在这里我们暂且把程序设计比喻为房子的布置,一间房子的布局中,需要各种功能的家具和洁具(类似方法),如马桶、浴缸、天然气灶,床、桌子等。

  • POP设计师:对于面向过程的程序设计更注重的是功能的实现(即功能方法的实现),效果符合预期就好,因此面向过程的程序设计会更倾向图1设置结构,各种功能都已实现,房子也就可以正常居住了
  • OOP设计师:但对于面向对象的程序设计则是无法忍受的,这样的设置使房子内的各种家具和洁具间摆放散乱并且相互暴露的机率大大增加,各种气味相互参杂,显然是很糟糕的,于是为了更优雅地设置房屋的布局,面向对象的程序设计便采用了图2的布局

对于面向对象程序设计来说这样设置好处是显而易见的,房子中的每个房间都有各自的名称和相应功能(在java程序设计中一般把类似这样的房间称为类,每个类代表着一种房间的抽象体),如卫生间是大小解和洗澡梳妆用的,卧室是休息用的,厨房则是做饭用的,每个小房间都各司其职并且无需时刻向外界暴露内部的结构,整个房间结构清晰,外界只需要知道这个房间并使用房间内提供的各项功能即可(方法调用),同时也更有利于后期的拓展了,毕竟哪个房间需要添加那些功能,其范围也有了限制,也就使职责更加明确了(单一责任原则)。

POP和OOP的联系

OOP的出现对POP确实存在很多颠覆性的,但并不能说POP已没有价值了,毕竟只是不同时代的产物,从方法论来讲,更喜欢将面向过程与面向对象看做是事物的两个方面–局部与整体(你必须要注意到局部与整体是相对的),因此在实际应用中,两者方法都同样重要。

了解完OOP和POP各自的特点,接着看java程序设计过程中OOP应用

Java中使用OOP

在java程序设计过程中,我们几乎享尽了OOP设计思想带来的甜头,以至于在这个一切皆对象,众生平等的世界里,狂欢不已,而OOP确实也遵循自身的宗旨即将数据及对数据的操作行为放在一起,作为一个相互依存、不可分割的整体,这个整体美其名曰:对象

利用该定义对于相同类型的对象进行分类、抽象后,得出共同的特征,从而形成了类,在java程序设计中这些类就是class

由于类(对象)基本都是现实世界存在的事物概念(如前面的不同的小房间)因此更接近人们对客观事物的认识,同时把数据和方法(算法)封装在一个类(对象)中,这样更有利于数据的安全,一般情况下属性和算法只单独属于某个类,从而使程序设计更简单,也更易于维护。

AOP的出现

基于这套理论思想,在实际的软件开发中,整个软件系统事实也是由系列相互依赖的对象所组成,而这些对象也是被抽象出来的类。相信大家在实际开发中是有所体验的(本篇文件假定读者已具备面向对象的开发思想包括封装、继承、多态的知识点)。但随着软件规模的增大,应用的逐渐升级,慢慢地,OOP也开始暴露出一些问题,现在不需要急于知道它们,通过案例,我们慢慢感受:
A类:

public class A {
    public void executeA(){
        //其他业务操作省略......
        recordLog();
    }

    public void recordLog(){
        //....记录日志并上报日志系统
    }
}

B类:

扫描二维码关注公众号,回复: 3834508 查看本文章
public class B {
    public void executeB(){
        //其他业务操作省略......
        recordLog();
    }

    public void recordLog(){
        //....记录日志并上报日志系统
    }
}

C类:

public class C {
    public void executeC(){
        //其他业务操作省略......
        recordLog();
    }

    public void recordLog(){
        //....记录日志并上报日志系统
    }
}

假设存在A、B、C三个类,需要对它们的方法访问进行日志记录,在代码中各种存在recordLog方法进行日志记录并上报,或许对现在的工程师来说几乎不可能写出如此糟糕的代码,但在OOP这样的写法是允许的,而且在OOP开始阶段这样的代码确实并大量存在着,直到工程师实在忍受不了一次修改,到处挖坟时(修改recordLog内容),才下定决心解决该问题,为了解决程序间过多冗余代码的问题,工程师便开始使用下面的编码方式

//A类
public class A {
    public void executeA(){
        //其他业务操作省略...... args 参数,一般会传递类名,方法名称 或信息(这样的信息一般不轻易改动)
        Report.recordLog(args ...);
    }
}

//B类
public class B {
    public void executeB(){
        //其他业务操作省略......
        Report.recordLog(args ...);
    }
}

//C类
public class C {
    public void executeC(){
        //其他业务操作省略......
        Report.recordLog(args ...);
    }
}

//record
public class Report {
    public static void recordLog(args ...){
        //....记录日志并上报日志系统
    }
}

另外一种是通过继承的方式,父类书写一份打印日志的方法即可。

显然代码冗余也得到了解决,这种通过继承抽取通用代码的方式也称为纵向拓展,与之对应的还有横向拓展(这就是AOP)。

AOP面向切面编程

事实上有了上述两种解决方案后,在大部分业务场景的代码冗余问题也得到了实实在在的解决,原理如下图:
在这里插入图片描述
但是随着软件开发的系统越来越复杂,工程师认识到,传统的OOP程序经常表现出一些不自然的现象,核心业务中总掺杂着一些不相关联的特殊业务,如日志记录,权限验证,事务控制,性能检测,错误信息检测等等,这些特殊业务可以说和核心业务没有根本上的关联而且核心业务也不关心它们,比如在用户管理模块中,该模块本身只关心与用户相关的业务信息处理,至于其他的业务完全可以不理会,我们看一个简单例子协助理解这个问题

public interface IUserService {

    void saveUser();

    void deleteUser();

    void findAllUser();
}
//实现类
public class UserServiceImpl implements IUserService {

    //核心数据成员

    //日志操作对象

    //权限管理对象

    //事务控制对象

    @Override
    public void saveUser() {

        //权限验证(假设权限验证丢在这里)

        //事务控制

        //日志操作

        //进行Dao层操作
        userDao.saveUser();

    }

    @Override
    public void deleteUser() {

    }

    @Override
    public void findAllUser() {

    }
}

上述代码中我们注意到一些问题,权限,日志,事务都不是用户管理的核心业务,也就是说用户管理模块除了要处理自身的核心业务外,还需要处理权限,日志,事务等待这些杂七杂八的不相干业务的外围操作,而且这些外围操作同样会在其他业务模块中出现,这样就会造成如下问题

  • 代码混乱:核心业务模块可能需要兼顾处理其他不相干的业务外围操作,这些外围操作可能会混乱核心操作的代码,而且当外围模块有重大修改时也会影响到核心模块,这显然是不合理的。
  • 代码分散和冗余:同样的功能代码,在其他的模块几乎随处可见,导致代码分散并且冗余度高。
  • 代码质量低扩展难:由于不太相关的业务代码混杂在一起,无法专注核心业务代码,当进行类似无关业务扩展时又会直接涉及到核心业务的代码,导致拓展性低。

显然前面分析的两种解决方案已束手无策了,那么该如何解决呢?事实上我们知道诸如日志,权限,事务,性能监测等业务几乎涉及到了所有的核心模块,如果把这些特殊的业务代码直接到核心业务模块的代码中就会造成上述的问题,而工程师更希望的是这些模块可以实现热插拔特性而且无需把外围的代码入侵到核心模块中,这样在日后的维护和扩展也将会有更佳的表现,假设现在我们把日志、权限、事务、性能监测等外围业务看作单独的关注点(也可以理解为单独的模块),每个关注点都可以在需要它们的时刻及时被运用而且无需提前整合到核心模块中,这种形式相当下图:
AOP
从图可以看出,每个关注点与核心业务模块分离,作为单独的功能,横切几个核心业务模块,这样的做的好处是显而易见的,每份功能代码不再单独入侵到核心业务类的代码中,即核心模块只需关注自己相关的业务,当需要外围业务(日志,权限,性能监测、事务控制)时,这些外围业务会通过一种特殊的技术自动应用到核心模块中,这些关注点有个特殊的名称,叫做“横切关注点”,上图也很好的表现出这个概念,另外这种抽象级别的技术也叫AOP(面向切面编程)

至于AOP在Java中的实现,可以参见我的下一篇博文,结合具体案例进行讲解

猜你喜欢

转载自blog.csdn.net/f641385712/article/details/83507404