f# - 在 F# 中实现 "services"的惯用方式

标签 f#

在 F# 中工作并实现“服务”模式时,例如围绕 Web API 或数据库或 USB 小工具的包装器,执行此操作的惯用方式是什么?我的倾向是使用例如IDatabaseDatabase , ITwitterTwitter , ICameraCamera就像 C# 中的接口(interface)和类,允许简单的测试模拟。但如果这不是标准的方法,我不想编写“F# 中的 C#”。

我考虑使用 DU 来表示例如Camera | TestCamera ,但这意味着将所有模拟代码放入生产代码库,这听起来很可怕。但也许这只是我的 OO 背景。

我也考虑过制作IDatabase功能记录。我仍然对这个想法持开放态度,但它似乎有点做作。此外,它排除了使用 IoC Controller 或任何“类似 MEF”的插件功能的想法(至少我知道)。

这样做的“惯用 F#”方式是什么?只遵循 C# 服务模式?

最佳答案

正如另一个答案中提到的,在 F# 中使用接口(interface)很好,这可能是解决问题的好方法。但是,如果您使用更多面向功能转换的编程风格,则可能不需要它们。

理想情况下,大多数实际有趣的代码应该是不执行任何效果的转换——因此它们不调用任何服务。相反,他们只是接受输入并产生输出。让我演示使用数据库。

使用 IDatabase服务,你可以这样写:

let readAveragePrice (database:IDatabase) = 
  [ for p in database.GetProducts() do
      if not p.IsDiscontinued then
        yield p.Price ]
  |> Seq.average

当这样写时,您可以提供 IDatabase 的模拟实现测试 readAveragePrice 中的平均逻辑是正确的。但是,您也可以像这样编写代码:
let calculateAveragePrice (products:seq<Product>) = 
  [ for p in products do
      if not p.IsDiscontinued then
        yield p.Price ]
  |> Seq.average

现在可以测试calculateAveragePrice没有任何 mock - 只需给它一些您想要处理的 sample 产品!这将数据访问从核心逻辑推到外部。所以你会有:
database.GetProducts() |> calculateAveragePrice // Actual call in the system
[ Product(123.4) ] |> calculateAveragePrice     // Call on sample data in the test

当然,这是一个简单的例子,但它显示了这个想法:

Push the data access code outside of the core logic and keep the core logic as pure functions that implement transformations. Then your core logic will be easy to test - given sample inputs, they should return the correct results!

关于f# - 在 F# 中实现 "services"的惯用方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32570410/

相关文章:

f# - 是否可以使用 F# 模式匹配作为另一种语言或 DSL 的求解器/库?

for-loop - 为什么闭包中允许使用 'for .. in'?

f# - 如何检查字符串是否只包含 F# 中的字母

f# - 如何避免在 F# 中双重包装到 Some

syntax - F# 创建和填充 FSharpx.Collections.Vector 的语法是什么?

nhibernate - NHibernate 中的受歧视联合体

haskell - 如何在现实世界中使用函数式编程?

.net - 如何使用 F# 的 Seq.cast 避免 "Value restriction"错误?

datetime - 如何在 F# 中将 DateTime 转换为 Unix TimeStamp?

algorithm - 使用函数式编程 (F#) 将位置列表转换为二维位置数组