Ajoo曾经写过面向组合子编程系列。我也帮着助威。
面向组合子编程和并不是简单意义上的Composite Pattern。Composite Pattern只是一个简单的基本Pattern。
面向组合子编程只是用到了Composite Pattern,面向组合子编程本身的内容复杂许多,以至于复杂到这样的程度,数据和行为必须分开,形成Visitor Pattern。
而一般意义上的Compositor Pattern都是数据和行为在一起的对象的组合。
用于面向组合子编程中,一般是指只有行为没有数据的Functor对象进行组合。
基本的模型是这样。包括3个参与部分。算法,Functor算子,参数。
算法基本上是固定的,Functor算子可能自由组合,参数也可能一个或者多个。
下面根据Functor和参数这两个方面介绍面向组合子编程的3种类型。
1. Functor组合 – Combinator, Pipe
Functor有多个,参数只有一个。
这一类的特点是,Functor移动,参数不动。
这是最常见的一类。
常见于用于工作流程控制的逻辑组合子。for each, or, and, not, if, else 等。
Hibernate的 Criteria Query 的 And , Or , Like, Equals的组合,也属于这一类。
ForEachCombinator( param ){ // And, Or for( functor in functors ){ … functor( param); … // do some thing. // 比如, or 的时候,第一个符合条件,就返回 // and 的时候,第一个不符合条件,就返回 } }
ForEachCombinator可以扩展为 OrCombinator,AndCombinator。
比较有趣的是,Pipe这种类型。
Pipe也是多个Functor,一个参数。但和上述的For each 之类的Combinator不同的是,下一个Functor的参数,是上一个Functor的返回结果。
Pipe( param ){ for( functor in functors ){ param = functor( param); // do some thing. } }
我们给Pipe模式加上一些逻辑判断,和其他的Combinator组合,就可以用来实现if, then之类复杂一些的条件组合逻辑。
比如,我们给Pipe加上一个中断条件。
Pipe( param ){ for( functor in functors ){ param = functor( param); if(param is not valid) // null, false, etc break; } }
pipe = new Pipe( {new Equls(1), new Printer });
表示参数是1的时候,会执行printer。
pipe(1) 就可以打。
pipe(2) 就不打印。
这里只是一个简单示意。比较详细的例子和实现,可以看ajoo的blog和帖子。
2. 参数集合 – Visitor, Map, Filter, Reduce
这一类比较有趣。参数是多个,是一个集合,Functor可以是一个(也可能是多个)。
这一类的特点是,Functor不动,参数移动。
FP的Map, Filter算法都是这样,接受一个集合参数(list, stream),都经过同一个Functor的处理,当然这个Functor本身可能是组合过的。
Reduce算法也是这样,只是有点不同。Reduce多了一个Continuation, Context作用的环境变量参数,这个参数上一次处理的结果,传递给同一个Functor的下一次调用。
自己产生参数,自己消费,这个行为模式,和Pipe有点类似。但是不同。
Reduce算法反复调用一个Functor,并遍历参数集合。Pipe调用多个Functor,而且每个Functor只调用一次。
3. Functor组合,参数集合 – Curry, Factory Chain
最复杂的类型,就是这一类了。Functor移动,参数也移动。
关于Curry的概念和例子,本文不展开。网上有不少好文章。可以搜索到。
Curry 很类似于 Factory Chain。
举个例子。F(x, y, z) = 2 * x + y - z。
F(1, 2, 3)
用Curry的方式写就是,f(1)(2)(3)。这是FP语法。
换成类似于c, java, c#的语法来写,就是Factory Chain的写法。
factory.generate(1).generate(2).generate(3);
这里的关键是,上一个Functor处理参数的结果产生了下一个Functor。
下一个Functor继续处理下一个参数,产生下一个Functor。
BigDecimal.add(…).add(…).multiply(…).substract(…)
StringBuffer.append(…).append(…)
factory.newSession(..).newQuery(…).setParameter(…).setParameter(…)
等等形式,都具有Curry, Factory Chain的形式特点。虽然有时候,Functor返回的是自己。
Design Pattern的 Abstract Factory ( Factory of Factor ) 可以扩展成 Factory of Factor of Factory .., 就成了Factory Chain。也具有Curry, Factory Chain的形式特点。
Curry, Factory Chain在问题降维(减少参数个数)方面有很大用处。