将函数分解为被动(算法)和活动(执行)对象

时间:2012-09-09 21:47:21

标签: python algorithm design-patterns architecture functional-programming

摘要

将纯函数拆分为被动对象的优缺点是什么,这些对象描述了可以执行这些算法的算法和活动对象?请注意,由于函数没有副作用,情况会大大简化。

详细

我正在编写的代码部分(在Python 3中)将主要遵循函数式编程。

有一些(不可变的)数据。有一些算法。我需要将这些算法应用于数据,并获得结果。

算法可以表示为常规函数,它将使用标准操作进行转换(例如,我可以组合两个函数,然后使用functools.partial冻结一些参数,然后将结果函数作为参数传递给另一个函数)。由于性能原因,许多较低级别的功能都会被记忆。

但是我想到了一个想法,也许我应该将算法表示为被动对象。这些对象本身无法执行任何操作。当我准备好执行时,我会将算法对象及其所需的所有输入提供给一个特殊的“计算”对象。这会比我的算法心智模型更好,但我担心我可能会错过这种方法的一些问题。

算法对象可以通过多种方式实现;也许甚至可以允许多个实现。假设我的算法是抽象类算法的实例;那么它的子类可以代表:

  • 我将创建的特定于域的语言中的文本字符串
  • 我将构建的某种执行树
  • 甚至是常规的Python函数

我之前从未这样做过,所以我希望得到一些关于这个想法的反馈。它是否提供任何真正的设计优势,除了我的主观感觉,它更“自然”?它会导致任何问题吗?

1 个答案:

答案 0 :(得分:1)

我认为设计没有任何重大优势或劣势。

假设任何计算对象都可以运行任何算法,那么你的类Algorithm可能会有一个名为execute的函数,它知道如何运行算法。命名函数__call__,现在您的Algorithm类与Python可调用对象(包括函数)完全相同。

对于您的DSL代码字符串:根据您的设计,您将它们表示为Algorithm的子类,它会覆盖execute以运行解释程序。在其他设计下,您只需执行以下操作:

def createDSLAlgorithm(code):
    def coderunner(*args, **kwargs):
        DSLInterpreter().interpret(code, *args, **kwargs)
    return coderunner

类似于创建一个函数,当被调用时将执行指定的表达式树。

当然,我可能会遗漏您计划在算法设计中添加的功能无法实现的功能。例如,不是所有 Python函数都具有可变属性。但是,由于用户定义的函数可以是闭包,可以具有属性,并且任何对象都可以表现得像一个函数"仅仅通过实施__call__,我怀疑同一事物的名称不同。

如果它有助于代码可读性,那么选择自己的名称当然是一个小优势。将属性附加到"对象"可能会感觉更自然一点。如果你的计算对象要询问算法的某些已知属性,以帮助决定在计算它们时做什么(例如是否记忆),那么将它们附加到"函数"