【问题标题】:How does the functional programming recommendation for static methods influence testability?静态方法的函数式编程建议如何影响可测试性?
【发布时间】:2012-01-08 15:47:51
【问题描述】:

我对函数式编程的深入研究越多,我就阅读了支持静态方法而不是非静态方法的建议。您可以在本书中阅读该建议,例如:

http://www.amazon.de/Functional-Programming-Techniques-Projects-Programmer/dp/0470744588

如果您考虑功能纯度,那当然是有道理的。一个静态函数站在那里说:“我不需要任何状态!”

但是,这对可测试性有何影响?我的意思是,一个有很多静态方法的系统是不是很难测试(因为静态方法很难模拟)?或者模拟在函数式编程中是否扮演次要角色?如果是:为什么?

编辑

因为有人怀疑这本书是否真的提出了这个建议。我会多引用一点。我希望 Oliver Sturm 没问题。

使用静态方法

静态方法是值得考虑作为一般准则的基本思想之一。许多面向对象的程序员都支持它,从功能的角度来看,大多数时候可以将函数设为静态。任何纯函数都可以设为静态。 (...)

有些人可能会争辩说,始终传递所有参数的想法意味着您没有尽可能多地利用面向对象的想法。这实际上可能是正确的,但也许是因为面向对象的概念没有像应有的那样考虑并行执行的问题。 (...)

最后,推荐一个准则:当您编写的方法不需要访问其所在类中的任何字段时,请将其设为静态!

顺便说一句,到目前为止已经有了很好的答案。谢谢!

【问题讨论】:

  • 事实上,我全心全意地反对,偏爱静态方法会使代码更实用。首先,静态方法当然可以像实例方法可以修改每个对象的状态一样轻松地修改静态状态 - 更改静态状态更糟糕,因为这是一个全局更改,不仅影响单个对象。其次,没有理由使用不修改任何状态的静态方法比使用不修改任何状态的非静态方法更有效。

标签: c# unit-testing functional-programming


【解决方案1】:

看待这一点的一种方式是,对于函数式编程,您只需要模拟特定函数所需的状态(通过提供合适的输入)。对于 OO 编程,您需要模拟类内部工作所需的所有状态。

函数式程序还有一个附带的好处,就是您可以保证用相同的输入重复相同的测试会得到相同的结果。在经典 OO 中,您不仅要保证相同的输入,还要保证相同的整体状态。

在架构良好的 OO 代码中,差异将很小(因为类将具有明确定义的职责),但功能测试的要求仍然是等效 OO 测试的严格子集。

(我意识到函数式编程风格可以通过不可变对象使用 OO - 请阅读上面提到的 OO 为“具有可变状态的面向对象编程”)

编辑:

正如Fredrik 所指出的,关于函数式方法的重要部分不是它们是静态的,而是它们不会改变程序的状态。 “纯”函数是从一组输入到一组输出的映射(相同的输入总是给出相同的结果),并且没有其他效果。

【讨论】:

    【解决方案2】:

    我认为静态方法本身不是问题,当它们开始对静态数据进行操作时,问题就来了。只要静态方法将输入作为参数,对其进行操作并返回结果,我认为测试它们没有问题。

    即使我不在代码中追求函数式方法,我也倾向于尽可能使方法静态化。但是在引入静态或者静态类型之前我觉得很仔细

    【讨论】:

      【解决方案3】:

      纯函数式编程中的所有“状态”都来自输入。要对功能程序进行单元测试,您需要创建测试输入并观察输出。如果您的方法无法通过给它们提供测试输入并观察输出来进行测试,那么它们的功能就不够了。

      【讨论】:

        【解决方案4】:

        在函数式编程中,您可能希望模拟函数而不是对象。所以如果你想在不依赖ComplicatedAndLongFunction的情况下测试函数f

        f(x)
        {
            myx = g(x);
            y = ComplicatedAndLongFunction(myx);
            myy = h(y)
            return myy;
        }
        

        您可能希望通过将 ComplicatedAndLongFunction 注入 f: 来将 fComplicatedAndLongFunction 分离:

        f(x, calc)
        {
            myx = g(x);
            y = calc(myx);
            myy = h(y)
            return myy;
        }
        

        因此您可以在测试中指定calc 的行为。

        这提出了一个问题(至少在我的脑海中),是否有模拟框架可以轻松指定函数的期望而不必恢复为对象。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-10
          • 2016-03-26
          • 1970-01-01
          • 1970-01-01
          • 2019-06-12
          相关资源
          最近更新 更多