xamarin - 函数式编程和依赖倒置 : how to abstract storage?

标签 xamarin f# functional-programming storage abstraction

我正在尝试创建一个具有较低级别库的解决方案,该库将知道在调用某些命令时需要保存和加载数据,但保存和加载功能的实现将在特定于平台的项目中提供它引用了较低级别的库。

我有一些模型,例如:

type User = { UserID: UserID
              Situations: SituationID list }

type Situation = { SituationID: SituationID }

我想要做的是能够定义和调用函数,例如:
do saveUser ()
let user = loadUser (UserID 57)

有什么方法可以在功能习语中清晰地定义它,最好同时避免可变状态(无论如何都不需要)?

一种方法可能看起来像这样:
type IStorage = {
    saveUser: User->unit;
    loadUser: UserID->User }

module Storage =
    // initialize save/load functions to "not yet implemented"
    let mutable storage = {
        saveUser = failwith "nyi";
        loadUser = failwith "nyi" }

// ....elsewhere:
do Storage.storage = { a real implementation of IStorage }
do Storage.storage.saveUser ()
let user = Storage.storage.loadUser (UserID 57)

这方面有一些变化,但我能想到的所有变化都涉及某种未初始化的状态。 (在 Xamarin 中,还有 DependencyService,但这本身就是我想避免的依赖项。)

有没有办法编写调用尚未实现的存储函数的代码,然后在不使用可变状态的情况下实现它?

(注意:这个问题与存储本身无关——这只是我正在使用的示例。它是关于如何在不使用不必要的可变状态的情况下注入(inject)函数。)

最佳答案

这里的其他答案可能会教您如何在 F# 中实现 IO monad,这当然是一种选择。不过,在 F# 中,我通常只使用 与其他函数组合函数 .您不必为了执行此操作而定义“接口(interface)”或任何特定类型。

开发您的系统由外而内 ,并通过关注它们需要实现的行为来定义您的高级函数。让他们高阶函数 通过将依赖项作为参数传递。

需要查询数据存储?传入 loadUser争论。需要保存用户吗?传入 saveUser争论:

let myHighLevelFunction loadUser saveUser (userId) =
    let user = loadUser (UserId userId)
    match user with
    | Some u ->
        let u' = doSomethingInterestingWith u
        saveUser u'
    | None -> ()
loadUser参数被推断为 User -> User option 类型, 和 saveUserUser -> unit , 因为 doSomethingInterestingWithUser -> User 类型的函数.

您现在可以“实现”loadUsersaveUser通过编写调用低级库的函数。

我对这种方法的典型 react 是:这需要我向函数传递太多参数!

确实,如果发生这种情况,请考虑这是否不是该功能试图做太多事情的气味。

由于Dependency Inversion Principle在这个问题的标题中提到,我想指出 SOLID principles如果所有这些都一起应用,效果最好。 Interface Segregation Principle说接口(interface)应该尽可能小,并且不会比每个“接口(interface)”都是单个函数时更小。

有关描述此技术的更详细的文章,您可以阅读我的 Type-Driven Development article .

关于xamarin - 函数式编程和依赖倒置 : how to abstract storage?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32411667/

相关文章:

xamarin - 使用 xamarin 表单在 ListView 上添加左右完全滑动手势

node.js - 使用 MongoDB Stitch 作为 Xamarin 应用程序的后端

Xamarin.Forms - 如何从 API 文档中计算出有效的 XAML 语法

f# - 如何将下载程序转换为异步?

generics - f# 可区分联合泛型

javascript - 循环直到...与 Ramda

xamarin - 尝试更改 iOS 手机上启动器图标下显示的名称

.net - Task.WaitAll 的线程局部对象

c - 内存分配中的函数式编程案例

scala - 更新元组列表