Lambda和策略模式
这是「从函数式角度看设计模式」的第一篇。目的是从和OOP不同的角度重新审视设计模式。
第一篇就从lambda说起了,因为这是函数式编程里面最常见的概念。在设计模式里,这个概念一般叫做「策略模式」。
按照定义,「策略模式是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换」。简而言之,就是不把算法写死,而是在运行时可以按需切换。一般来说,OOP的写法大概如下:
1 |
|
简而言之,「策略模式建议找出负责用许多不同方式完成特定任务的类, 然后将其中的算法抽取到一组被称为策略的独立类中」。
不过,如果换个角度讲,从函数式编程的角度,「算法可以互相替换」其实很简单?只要为每个算法设计一个函数,然后再在主函数里增加一个高阶函数参数,接受这样的算法作为入参,那就可以了。
1 |
|
这里用一个函数式语言展示了一种经典的策略模式的使用场景。不过,就策略模式而言,如果追求的是运行时动态切换的能力,那么显然的,函数式语言提供了走的更远的可能性:
- 在策略模式里,切换算法的可能性被耦合进了继承关系中,这体现在从策略基类派生出具体算法的这一层继承关系里,也体现在「『客户端』必须知晓策略之间的差异并且选择具体的策略」这一层实现里。
- 在高阶函数作为参数传入的函数式写法中,并没有一个需要去派生的策略基类,算法之间的关系和算法与客户端(主函数,例如这里的
someProcess
)之间的关系仅仅是函数类型需要一致。这就让进一步的解耦成为了可能。
虽然这里我们也使用了FilterStrategy
来枚举不同的具体算法,但实际上,这个枚举可以虚化,被一个「从关键词映射到具体算法」的字典所替代。也就是说,不需要预先写死代码,而在运行时可以自行选择算法,也就成为了可能。当然,这种解耦是否真的需要,也是因人而异,并且因为不同场景而有不同的要求的。
但不管如何,这也体现出函数式编程比起OOP的一个优点,那就是基于代数关系之间的结合,而不是依赖继承,从而有着更灵活的可能性。
实际上,这也可以理解为一种控制反转吧,就是说在一个函数里不去具体定义操作,而是把具体的实现注入进来。
有趣的是,在例如Java这样的OOP语言里,lambda本身只不过是SAM匿名类的语法糖,而SAM,或者说,「Functional Interface」的结构又和策略模式的写法是很相似的。这也许也提示了策略模式可以被lambda所取代的特点。