OOP 与 AOP

这篇文章是为了弄清楚 面向对象(OOP)、面向过程(OPP)、面向切面(AOP) 这三者的概念。


OOP

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。

面向对象是由面向过程演化而来,典型的面向过程语言就是 C,而 Java、C++、Python 等都是面向对象的编程语言。


面向过程与面向对象

面向过程是指考虑整个过程的步骤。

比如,就拿吃饭这个问题来说,如果是面向过程,那么就会有以下的算法步骤:

  1. 买菜
  2. 洗菜
  3. 炒菜
  4. 吃饭

可以看到,面向过程就是注重整个过程,你必须要考虑到所有步骤,而且一步也不能出错。

面向对象就是把一类事物抽象出来,变成一个整体对象来考虑。

如果是面向对象,那吃饭这个问题就这么考虑:

  1. 去餐馆点菜
  2. 吃饭

这里的餐馆就是一个对象,这个对象具有买菜、洗菜、炒菜的功能,而我们就只用关心这个对象,它能解决吃饭的问题,完全不用关心炒菜等问题。

面向对象符合人的思考习惯,更便于理解事物。

现在总结出面向对象的三个特征是:封装、继承、多态。

弊端

即便是如此,面向对象也不是万能的。面向对象也有局限性,就拿 Bean 对象来说,因为面向对象的编程思想就必须尽量满足封装的特性,封装之后,外部访问 Bean 就必须通过 get()、 set() 方法,而这种方式,就会导致程序性能的下降。

面向对象的出现是为了简化问题的复杂度,但在某些场景下也会增加复杂度。

详细情况可了解 面向对象编程的弊端是什么?



AOP

AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过 预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。


在 OOP 的思想下,我们会将系统在横向上拆分成多个模块,每个模块都相对独立,都有各自的职责划分。OOP 的精髓是把功能或问题模块化,每个模块处理自己的家务事。但在现实世界中,并不是所有问题都能完美得划分到模块中。

举个最简单而又常见的例子:现在想为每个模块加上日志功能,要求模块运行时候能输出日志。

按照 OOP 的思想来说,肯定是添加一个 日志模块,然后定义好对外提供的方法,然后让每个使用此功能的模块都来依赖日志模块,并自行使用日志输出功能。

但这样划分模块真的完美吗?

从OOP角度看,除了日志模块本身,其他模块的家务事绝大部分情况下应该都不会包含日志输出功能,但众多模块确实又需要打印日志。这个日志输出功能,从整体来看,都是一个上的。而这个面的范围,就不局限在单个模块里了,而是横跨多个模块。


AOP 就是为了解决这种面的问题。

  • 第一,我们要认识到OOP世界中,有些功能是横跨并嵌入众多模块里的,比如打印日志,比如统计某个模块中某些函数的执行时间等。这些功能在各个模块里分散得很厉害,可能到处都能见到。
  • 第二,AOP的目标是把这些功能集中起来,放到一个统一的地方来控制和管理。如果说,OOP如果是把问题划分到单个模块的话,那么AOP就是把涉及到众多模块的某一类问题进行统一管理。

AOP 的做法就是: 在预编译或者运行时,动态地将统一的代码切入到类的指定方法、指定位置上。

一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。

所有说,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。

在 Spring 框架中,就可以看到大量的使用了 AOP 的相关技术。

在Android端,近年来也有许多应用。



Android 中的 AOP

应用场景

AOP 在 Android 中的主要应用场景如下:

  • 权限检查
  • 日志记录
  • 性能监控
  • 埋点操作

这些都是最常见的,当然,你还可以发掘出更多的应用场景。


应用工具

AOP 也就是一种编程思想,如果要落实到具体的编码上,在Android 中则常常借助于 APT、AspectJ、Javassist 这三个工具。

在源码的各个时期,它们的作用如下图:

APT,AspectJ,Javassist对应的编译时期


APT

APT(Annotation Processing Tool),注解处理器。

定义源码期的注解,然后继承 Proccesor 并通过查找特点注解,实现代码生成逻辑,利用 Java 编译器实现了编译期生成 .java 文件。

在Android中,APT 应用的代表框架有:DataBinding,Dagger2, ButterKnife, EventBus3 、DBFlow、AndroidAnnotation。


AspectJ

AspectJ 支持将 .java 文件编译为 .class 文件时的编译期注入代码。

AspectJ 语法比较多,但是掌握几个简单常用的,就能实现绝大多数切片,完全兼容 Java。

AspectJ 除了 hook 之外,AspectJ 还可以为目标类添加变量,接口。另外,AspectJ也有抽象,继承等各种更高级的玩法。

AspectJ 最具代表性的应用就是 Jake Wharton 的 Hugo


Javassist

Javassist 是对已经编译好的class文件进行操作。

它需要结合 AS 的 gradle plugin 来使用,主要用于各大热修复框架 HotFix、



参考