可控函数

可控函数(Controllable Function),是指那种能够经过改变函数的参数或者简单的修改函数在执行过程当中的外部依赖的而产生肯定结果的一类函数,这是我根据纯函数的概念推广出来的一个新的名词(大概是新的吧?)。编程

为何须要可控函数

纯函数在函数式编程的世界中很是的广泛,并且最近它也逐渐在向面向对象的世界中展示出愈来愈多的魅力,你甚至能够在一些框架或者技术的文档中看到文档做者对纯函数的推崇。在我看来,纯函数可以吸引到更多开发者的关注的一个重要的缘由就是它更容易测试,由于咱们能够很是确定的肯定在给定输入的状况下纯函数的输出。可是在面向对象的世界中,由于有类的存在,因此咱们常常编写的函数(或者说是方法)很难设计成为纯函数 —— 毕竟咱们老是会在类的方法中访问类的其余成员。框架

而可控函数则在对外界环境的访问以及修改的限制较为宽松。不知你是否还记得人教版高中生物书中提到的“控制变量法”,在设计实验的时候,咱们须要尽量地作到仅改变单个对试验结果产生影响的变量。编写单元测试也是这样,若是咱们可以很是简单的控制某个函数的所有依赖(包括函数参数以及外部依赖,例如,类的其余成员)的变化,那么咱们就能够很容易地使用控制变量法设计咱们的测试用例,并简化划分等价类的过程,这对于编写白盒测试来讲,好处不言而喻。编程语言

是什么让你的函数不可控

若是函数依赖“系统当前时间”这样的没法使用代码来控制的外部变量的话,那么这个函数经常会变得不可控。由于在大多数的编程语言或者技术框架中,修改当前系统时间或者 Mock 获取系统时间的全局变量每每是很困难的。像 JS 能够很是容易的 Mock window.Date 类,可是在 C# 中,Mock DateTime 会很是的艰难。ide

另外一种让函数成为脱缰野马的状况极可能是它依赖了过于复杂的外部对象,例如 EF Core 的 DbContextDbContext 很难被完美的 Mock,不论是 InMemory 仍是 SQLite InMemory,都有其限制所在。就更不要说查询姿式(是否 AsNoTracking,是否使用了第三方 EF 拓展)会对真正的执行过程产生的影响了。原本没有那么复杂逻辑,却由于引入的复杂的外部对象而变得难以预测了。函数式编程

从新掌控你的函数

在这里我想先介绍一款编程语言 —— Elm,在 Elm 的基础类库中,它很反常的没有提供直接获取系统时间的函数,相反地,Elm 的设计者认为获取系统时间并非一个纯函数,由于咱们没办法在不一样时刻让这个函数返回相同的值。为了描述这一现象,Elm 将这个函数实现为了一种反作用,简单来讲就是开发者须要是使用相似于 Callback 的机制来获取系统当前时间,这样就强迫咱们解除了对系统当前时间的强依赖,由于咱们只能经过 Callback 的参数来获取真正的系统时间。函数

模仿 Elm 的理念,在使用咱们没法使用代码来控制的外部变量的时候,咱们可使用“包装一层”的方法来解决强依赖的问题。例如,在面向对象的世界中,能够经过使用一个 ISystemTimeProvider 接口来获取当前的系统时间,它就像 Elm 中的 Callback 同样,躲在一个抽象层后面,默默无闻地为咱们的代码提供当前系统的准确时间。单元测试

除了上面提到的咱们没法控制的外部依赖以外,还有一些外部依赖是咱们难以控制的,它们每每来自于咱们所使用的第三方类库,而且它们的组成也一般是比较复杂的,例如 AspNetCore 中的 HttpContext 以及 DbContext。能够想象,若是一个函数接收这种类型的参数,那么如何去建立一个指望的输入就变成了一件困难的事了。这种时候咱们能够试着 Keep it simple and stupid。例如,当咱们只须要使用 DbContext 的查询结果的时候,直接让查询函数返回来自 BCL 的类型而不是 IQueryable<T>,这样,依赖查询结果的函数就能够摆脱对 DbContext 的依赖了。测试

相关文章
相关标签/搜索