gpt4 book ai didi

dependency-injection - 什么时候使用接口(interface),什么时候使用高阶函数?

转载 作者:行者123 更新时间:2023-12-04 01:04:08 34 4
gpt4 key购买 nike

给定一个具有以下层的 ASP.NET MVC 应用程序:

  • UI( View 、CSS、Javascript 等)
  • Controller
  • 服务(包含业务逻辑和数据访问)

  • 没有单独的数据访问层的原因是我使用的是 SQL 类型提供程序。

    (以下代码可能不起作用,因为它只是一个原始草稿)。
    现在想象一个名为 UserService 的服务定义如下:
    module UserService =
    let getAll memoize f =
    memoize(fun _ -> f)

    let tryGetByID id f memoize =
    memoize(fun _ -> f id)

    let add evict f name keyToEvict =
    let result = f name
    evict keyToEvict
    result

    然后在我的 Controller 层中,我将有另一个名为 UserImpl 的模块。或者也可以将其命名为 UserMemCache :
    module UserImpl =   
    let keyFor = MemCache.keyFor
    let inline memoize args =
    MemCache.keyForCurrent args
    |> CacheHelpers.memoize0 MemCache.tryGet MemCache.store

    let getAll = memoize [] |> UserService.getAll
    let tryGetByID id = memoize [id] |> UserService.tryGetByID id
    let add =
    keyFor <@ getAll @> [id]
    |> UserService.add MemCache.evict

    它的用法如下:
    type UserController() =
    inherit Controller()

    let ctx = dbSchema.GetDataContext()

    member x.GetAll() = UserImpl.getAll ctx.Users
    member x.UserNumberOne = UserImpl.tryGetByID ctx.Users 1
    member x.UserNumberTwo = UserImpl.tryGetByID ctx.Users 2
    member x.Add(name) = UserImpl.add ctx.Users name

    使用接口(interface),我们将有以下实现:
    type UserService(ICacheProvider cacheProvider, ITable<User> db) =
    member x.GetAll() =
    cacheProvider.memoize(fun _ -> db |> List.ofSeq)

    member x.TryGetByID id =
    cacheProvider.memoize(fun _ -> db |> Query.tryFirst <@ fun z -> z.ID = ID @>)

    member x.Add name =
    let result = db.Add name
    cacheProvider.evict <@ x.GetAll() @> []
    result

    用法如下:
    type UserController(ICacheProvider cacheProvider) =
    inherit Controller()

    let ctx = dbSchema.GetDataContext()
    let userService = new UserService(cacheProvider, ctx.Users)

    member x.GetAll() = userService.GetAll()
    member x.UserNumberOne = userService.TryGetByID 1
    member x.UserNumberTwo = userService.TryGetByID 2

    显然接口(interface)实现的代码要少得多,但它不再像函数式代码了。如果我开始在整个 Web 应用程序中使用接口(interface),我什么时候知道何时使用高阶函数? - 否则我只会得到一个普通的旧 OOP 解决方案。

    简而言之:什么时候应该使用接口(interface),什么时候使用高阶函数? - 必须画一些线,否则都是类型和接口(interface),FP 的美感就消失了。

    最佳答案

    在接口(interface)上。 首先,我认为您可以将接口(interface)视为只是命名的函数对。如果你有:

    type ICacheProvider =
    abstract Get : string -> option<obj>
    abstract Set : string * obj -> unit

    那么这几乎等同于拥有一对(或一条记录)函数:
    type CacheProvider = (string -> option<obj>) * (string * obj -> unit)

    使用接口(interface)的好处是您可以为类型命名(您也可以通过记录获得)并且您可以更清楚地表达您的意图(其他组件可以实现该接口(interface))。

    我认为如果你有两个以上的函数经常一起传递给其他函数,我认为使用接口(interface)是一个好主意——这样可以避免参数过多。

    模块或类。 代码中的真正区别在于是使用具有高阶函数的模块还是将接口(interface)作为构造函数参数的类。 F# 是一种结合了函数式和 OO 风格的多范式语言,所以我认为以这种方式使用类是非常好的。 (在定义数据类型来表示域等时,您仍然可以从函数式风格中受益。)

    要记住的一件事是,函数式编程就是关于组合的。在这种情况下,这可能没有那么有用,但我总是更喜欢编写可以编写以添加更多功能的代码,而不是在我想使用它时需要我提供某些东西的代码。

    也许您可以编写它,以便您的数据库访问代码不直接进行缓存(这将包括所有数据库查询和预处理逻辑):
    module UserService =
    let getAll () = (...)
    let tryGetByID id = (...)
    let add name = (...)

    ...然后定义一个包装它并添加缓存的类型(然后它将由 Web 应用程序的主要类型使用 - 它与您在示例中定义的类型非常相似,但现在我们正在分离数据库使用缓存提供程序进行访问和内存):
    type UserService(cacheProvider:ICacheProvider) =
    member x.GetAll() = cacheProvider.memoize UserSerivce.getAll ()
    member x.TryGetByID id = cacheProvider.memoize UserService.tryGetByID id
    member x.Add name = cacheProvider.memoize UserService.add name

    概括。 但是 - 我认为你的方法使用一个需要 ICacheProvider 的类非常好 - F# 在混合功能和面向对象的风格方面非常好。我发布的示例实际上只是一个可能的扩展,可能在更大的项目中有用(如果您想使用功能方面并明确区分功能的不同方面)

    关于dependency-injection - 什么时候使用接口(interface),什么时候使用高阶函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18458640/

    34 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com