1. 回顾 AOP 是什么?
维基百科解释如下:
面向切面的程序设计(Aspect-oriented programming,AOP,又译作面向方面的程序设计、剖面导向程序设计)是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知(Advice)机制,能够对被声明为“切点(Pointcut)”的代码块进行统一管理与装饰,如“对所有方法名以‘set*’开头的方法添加后台日志”。该思想使得开发人员能够将与代码核心业务逻辑关系不那么密切的功能(如日志功能)添加至程序中,同时又不降低业务代码的可读性。面向切面的程序设计思想也是面向切面软件开发的基础。
面向切面的程序设计将代码逻辑切分为不同的模块(即关注点(Concern),一段特定的逻辑功能)。几乎所有的编程思想都涉及代码功能的分类,将各个关注点封装成独立的抽象模块(如函数、过程、模块、类以及方法等),后者又可供进一步实现、封装和重写。部分关注点“横切”程序代码中的数个模块,即在多个模块中都有出现,它们即被称作“横切关注点(Cross-cutting concerns, Horizontal concerns)”。
日志功能即是横切关注点的一个典型案例,因为日志功能往往横跨系统中的每个业务模块,即“横切”所有有日志需求的类及方法体。而对于一个信用卡应用程序来说,存款、取款、帐单管理是它的核心关注点,日志和持久化将成为横切整个对象结构的横切关注点。
参见: https://zh.wikipedia.org/wiki/面向切面的程序设计
简单来说,就是功能上我们要加其他感觉和原本功能无关的逻辑,比如性能日志,代码混在一起,看着不爽,影响我们理解。
举个例子, 如下代码我们要多花几眼时间才能看明白:
public int doAMethod(int n) { int sum = 0; for (int i = 1; i <= n; i++) { if (n % i == 0) { sum += 1; } } if (sum == 2) { return sum; } else { return -1; } }然后我们需要记录一系列日志,就会变成这样子:
public int doAMethod(int n,Logger logger, HttpContext c, .....) { log.LogInfo($" n is {n}."); log.LogInfo($" who call {c.RequestUrl}."); log.LogInfo($" QueryString {c.QueryString}."); log.LogInfo($" Ip {c.Ip}."); log.LogInfo($" start {Datetime.Now}."); int sum = 0; for (int i = 1; i <= n; i++) { if (n % i == 0) { sum += 1; } } if (sum == 2) { return sum; } else { return -1; } log.LogInfo($" end {Datetime.Now}."); }一下子这个方法就复杂多了,至少调用它还得找一堆貌似和方法无关的参数
AOP 的想法就是把上述方法拆分开, 让log之类的方法不在我们眼中:
public int doAMethod(int n) { int sum = 0; for (int i = 1; i <= n; i++) { if (n % i == 0) { sum += 1; } } if (sum == 2) { return sum; } else { return -1; } }AOP 让看着只调用的 doAMethod 方法实际为:
public int doAMethodWithAOP(int n,Logger logger, HttpContext c, .....) { log.LogInfo($" n is {n}."); log.LogInfo($" who call {c.RequestUrl}."); log.LogInfo($" QueryString {c.QueryString}."); log.LogInfo($" Ip {c.Ip}."); log.LogInfo($" start {Datetime.Now}."); return doAMethod(n); log.LogInfo($" end {Datetime.Now}."); }所以AOP 实际就是干这个事情,
无论语言,
无论实现,
其实只要干这个事不就是AOP吗?
2. 类似AOP想法的实现方式分类达到AOP要做的这种事情有很多种方法,下面来做个简单分类,不一定很全面哦
2.1 按照方式 2.1.1 元编程很多语言都有内置类似这样一些“增强代码”的功能,
一般来说,从安全性和编译问题等角度考虑,大多数元编程都只允许新增代码,不允许修改。
这种都是编译器必须有才能做到。(没有的,你也可以自己写个编译器,只要你做的到)
当然元编程的概念不仅仅可以用来做类似AOP的事情,
还可以做各种你想做的事情,(只要在限制范围内能做的)
以下的例子就是生成一些新的方法。
宏例如 Rust / C++ 等等都具有这样的功能
例如 Rust 的文档:https://doc.rust-lang.org/stable/book/ch19-06-macros.html
use hello_macro::HelloMacro; use hello_macro_derive::HelloMacro; #[derive(HelloMacro)] struct Pancakes; fn main() { Pancakes::hello_macro(); }宏实现
extern crate proc_macro; use crate::proc_macro::TokenStream; use quote::quote; use syn; #[proc_macro_derive(HelloMacro)] pub fn hello_macro_derive(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); impl_hello_macro(&ast) } csharp 的 Source Generators新的实验特性,还在设计修改变化中