首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件开发 >

函数式思想: 函数设计模式,第 1 部分

2012-09-09 
函数式思维: 函数设计模式,第 1 部分函数世界中的一些经验主义者认为设计模式的概念有缺陷,在函数式编程中

函数式思维: 函数设计模式,第 1 部分

函数世界中的一些经验主义者认为设计模式的概念有缺陷,在函数式编程中不需要。在模式?的狭义解释下该观点可能成立,但这是一个更多关于语义而非使用的论点。设计模式的概念(针对常见问题的指定编目解决方案)是合理的。但是,模式有时在不同的范式下以不同的形式出现。因为构建块和问题解决方法在函数世界中是不同的,一些传统的 Gang of Four 模式(参阅?参考资料)消失了,而其他模式存在问题,但解决问题的方式大相径庭。本期和下一期将研究一些传统的设计模式,并以函数式思维从全新角度来思考它们。

在函数编程领域,传统设计模式通常以三种方式之一表现:

  • 模式由语言吸收。
  • 模式解决方案仍然存在于函数范式中,但是实现细节有所不同。
  • 解决方案使用其他语言或范式缺乏的功能实现。(例如,许多使用元编程的解决方案简洁且优雅,但无法通过 Java 实现。)

    我会依次研究这三种情况,在本期中从一些熟悉的模式入手,大部分模式全部或部分地纳入现代语言。

    工厂和局部套用(currying)

    局部套用 (Currying)?是许多函数语言的一种特性。它是以数学家 Haskell Curry 的名字命名的(Haskell 编程语言也是以该数学家命名),能够对多参数函数进行转换,以便将它用作一串单参数函数进行调用。与此密切相关的是部分应用 (partial application),该技术可以将固定值分配给函数的一个或多个参数,从而生成另一个更小的元数 (arity)?函数(元数是函数参数的个数)。我在 “函数式思维:运用函数式思维,第 3 部分” 中讨论过这两种技术。

    在设计模式上下文中,局部套用充当一个函数工厂。函数式编程语言中的一个常见特性是一等(first-class)(或高阶)函数,它允许函数充当任何其他数据结构。多亏这一特性,我可以轻松创建基于一些条件返回其他函数的函数,这就是工厂的精髓。例如,如果您有一个将两个数字相加的通用函数,您可以将局部套用用作一个工厂来创建总是将其参数加 1 的函数,即一个增量器,如清单 1 所示,使用 Groovy 语言实现:


    清单 1. 局部套用作为函数工厂

    在?清单 1?中,我将第一个参数局部套用为?1,返回一个接受单一参数的函数。本质上,我创建了一个函数工厂。

    当您的语言本机支持这种行为时,它往往被用作其他大小对象的构建块。例如,看看如清单 2 所示的 Scala 示例:


    清单 2. Scala 对局部套用的 “随意” 使用
    ?

    一等函数(First-class)和设计模式

    一等函数大大简化了许多常用的设计模式。(命令设计模式甚至消失了,因为您不再需要一个针对可移植功能的对象包装器。)

    模板方法

    一等函数使模板方法设计模式(参阅?参考资料)更易于实现,因为它们能够移除可能不需要的结构。模板方法定义一个方法中算法的框架,把一些步骤委托给子类,并强制他们在不更改算法结构的情况下定义这些步骤。模板方法的典型实现如清单 3 所示,使用 Groovy 实现:


    清单 3. 模板方法的 “标准” 实现

    在?清单 4?中,算法中的步骤只是类的属性,像任何其他属性一样是可分配的。在这个示例中,语言特性主要地吸收实现细节。将这一模式看作一个问题的解决方案(把步骤委派给后续的处理程序)仍然很有用,不过实现起来比较简单。

    两种解决方案不是等同的。在?清单 3?中的 “传统” 模板方法示例中,抽象类需要子类来实现依赖的方法。当然,子类可能仅创建一个空的方法体,不过抽象方法定义形成一种文档,提醒 subclasser 将其考虑在内。另一方面,死板的方法声明可能不适合于需要更多灵活性的情景中。例如,我可以创建我的?Customer?类的一个版本,该类接受任何方法列表以供进行处理。

    对代码块等功能的深度支持使语言更具有开发人员友好性。考虑这样一种情况,比如您想让 subclasser 跳过一些步骤。Groovy 有一种特殊的受保护访问运算符 (?.),该运算符确保在调用一个对象的方法前该对象不为空。考虑清单 5 中的?process()?定义:


    清单 5. 添加对代码块调用的保护
    策略

    一等函数简化的另一种流行设计模式是策略模式。策略定义一系列算法,封装每一种算法并使它们能够进行互换。它允许算法随使用它的客户不同而有所不同。一等函数使得构建和操作策略更简单。

    用于计算产品数目的策略设计模式的一种传统实现如清单 6 所示:


    清单 6. 为具有两个数目的产品使用策略设计模式
    ?

    享元 (Flyweight) 和内存化 (memoization)

    享元模式是一种使用共享来支持大量细粒度对象引用的优化技术。您要保持对象池可用,为特定视图创建到该池的引用。享元使用一种规范对象(canonical object)?的思想,即一种表示该类型中所有其他对象的代表性对象。例如,您有一个特定的消费产品,产品的规范版本表示该类型的所有产品。在一个应用程序中,不要为每个用户创建一个产品列表,而要创建一个规范产品列表,每个用户拥有对他们产品列表的引用。

    考虑清单 9 中建模计算机类型的类:


    清单 9. 建模计算机类型的简单类
    ?

    结束语

    在本期中,我介绍了设计模式的语义在函数式编程中表现的三种方式。首先,它们可以被语言或运行时吸收。我使用工厂、策略、单例和模板方法模式展示了相关示例。其次,模式可保留其语义,但具有完全不同的实现;我展示了使用类和使用内存化的享元模式示例。第三,函数语言和运行时可以有完全不同的特性,从而支持它们以完全不同的方式解决问题。

    在下一期,我将继续研究设计模式和函数式编程的交叉,并展示第三种方法的示例。

热点排行