从OO到AOP再到FP

我的疑问


面向对象编程和函数式编程的最大不同点在什么地方?

  • 数据从何而来?面向对象编程中的数据都是对象本身产生的,也就是说对象的基础建立在它本身的属性上,没有属性,那么这个对象什么都不能做。函数式(闭包)的过程数据来自于外部,也就是说闭包本身是不维护数据的。这样的好处是只管逻辑,而不用维护数据本身复杂的状态。

  • 数据的状态如何?面向对象编程中数据(也就是属性)是可以改变的,而函数式中数据是不可以改变的。

  • 没有属性的对象,还是面向对象嘛?我认为controller, service, DAO(repository)其实都是没有数据的对象!也就是说这些分层结构其实都是函数式编程可以应用的地方。有人会说奇怪了,controller中明明有service属性,service中明明有DAO属性啊,怎么能说没有数据呢?其实,这个只是调用链(我喜欢把它们看作是filter),真正的数据(大概被称为model)是被一层层传递下去的。可能又有人会反驳,函数式中最大的特点就是数据不可变,这些真正的数据(model)在传递过程中其实是会被改变的,所以怎么能说这些地方可以应用函数式编程?我认为,这些数据在各个层上应该是不同类的。展现给view的,和存储到database中的model是很难相同的,所以也就存在一种模型之间转化的可能,一个模型变成另一个新的模型,模型本身是不会变的。

  • 没有方法的值对象呢?就是数据吧。

AOP和函数式编程有没有关系?


AOP是什么?

AOP是面向切面编程。我们平时编写web程序的时候,通常都会使用controller->service->dao这样的层次结构的调用链。事实上,有些逻辑不是纵向延伸的,对于单个的层级,如service,一个典型的应用就是Log处理:我们需要对service中涉及写入数据库的操作进行记录,这样的逻辑对于service上的所有需要log记录的方法而言是通用的。所以没有必要在各个不同的services上手工记录log,我们只需要将这部分log的逻辑抽出来作为独立的方法,然后很形象地表述成在services上的方法打上一个个洞,将这部分独立的方法像插槽(阀门)一样插入这些洞里,流过services上这些被记录的方法上的数据及其本身就会被完整的记录下来。

AOP的概念也很有趣。事实上,AOP中的Joinpoint,Pointcut,Advice可以分别类比为钩子,晾衣杆和晾衣服这个动作。Joinpoint就是要拿来鼓捣的方法,Pointcut则是将这些方法串起来,Advice是统一处理这些方法的逻辑操作,同时也包括了它自身的执行时机,是在Pointcut之前、之后或是其中,当然更有甚者,是在方法返回之后,还是抛出异常之后呢?

那么接下来,我们来看看AOP和函数式编程到底有没有关系。第一个方面,AOP其实是间接地拿函数作为自己的参数了。举个例子,拦截controller的方法:

1
2
3
4
5
6
7
@AfterReturning(value = "execution(* controller.*.*(..)) && args(customer)",
returning = "result")
public void aMethod(Customer customer, String result) {
System.out.println("before");
System.out.println(result);
System.out.println("after");
}

其实这样的方式——既拿到了输入参数,又拿到了输出参数,就等于已经拿到了整个方法,何况还有牛掰的@Around,直接拿人家的方法到自己的方法体里执行。而函数式第一要点就是函数是一等公民,函数可以作为参数传递。所以说从获取参数这个层面说,两者实在太相像了。

第二个方面,抽取共有逻辑。函数式中有惰性求值,假设我们在函数式中预留一个Function作为钩子,任何想使用这部分函数逻辑的在需要的时候,通过某种机制传递真正的Function到我们的预留参数中,是不是也能实现抽取共有逻辑呢?还有,我们之所以有很多services,是因为model(domain)存在差异,假设函数式中没有OO的概念,model似乎就趋同了,那么或许我们根本不需要services。所以函数式是可以抽取共有逻辑的,或者说这部分逻辑本来就不应该分散到各个services中。

结论处,我大胆地猜测:AOP其实就是OO被掣肘之后,利用函数式缝补自身的妥协方案。AOP本来就是Functional programming.

Functional Programming的要点


  • 声明式编程
  • 不变量
  • 无副作用的函数
  • 不会依附于任何类或者对象,这点“functional programming for java developer”中有清楚描述。在java中写的大部分函数式代码都是静态的,但逻辑上,它不属于这个类。