F#应用程序结构日志/存储库等

Dyl*_*lan 5 architecture f# functional-dependencies

我在很多家庭项目中逐渐转向F#,但我对于如何将完整的应用程序连接在一起更加困难,尤其是跨领域的问题.

在C#中,如果我想记录内容,我会使用依赖注入将ILogger传递到每个类中,然后可以从代码中轻松地调用它.我可以在我的测试中验证给定特定情况的日志是通过传入模拟并验证它来编写的.

public class MyClass
{
    readonly ILogger _logger;
    public MyClass(ILogger logger)
    {
        _logger = logger;
    }

    public int Divide(int x, int y)
    {
        if(y == 0)
        {
            _logger.Warn("y was 0");
            return 0;
        }
        return x / y;
    }
}
Run Code Online (Sandbox Code Playgroud)

在F#我使用的模块更多,所以上面会变成

module Stuff

let divde x y =
    match y with 
    | 0 -> 0
    | _ -> x / y
Run Code Online (Sandbox Code Playgroud)

现在,如果我有一个名为Logging的模块,我可以打开它并在y为0的情况下使用日志函数,但是如何将其注入单元测试?

我可以让每个函数都使用一个日志函数(字符串 - >单位),然后使用部分应用程序连接它们,但这似乎是一项非常多的工作,就像创建一个新的函数来包装日志调用中的实际调用一样.是否有特定的模式或一点F#我缺少可以做到的?(我已经看过kprintf函数,但我仍然不知道你如何为各种测试场景指定函数,同时使用完整应用程序的具体实现)

同样,您如何存根获取数据的存储库?你需要实例化一些类并在其上设置CRUD函数,还是有一种方法可以注入你打开的模块(除了#define)

Dan*_*iel 3

这是一个基本的答案。首先,您似乎认为类和模块是可以互换的。类封装数据,从这个意义上讲,类更类似于记录和 DU。另一方面,模块封装功能(它们被编译为静态类)。所以,我认为你已经提到了你的选择:部分函数应用程序,将函数作为数据传递,或者......依赖注入。对于您的具体情况,保留您所拥有的似乎是最简单的。

另一种方法是使用预处理器指令来包含不同的模块。

#if TESTING 
open LogA
#else
open LogB
#endif
Run Code Online (Sandbox Code Playgroud)

DI 不一定不适合函数式语言。值得指出的是,F# 使定义和实现接口比 C# 更容易。