Dependency injection 何时使用接口,何时使用高阶函数?

Dependency injection 何时使用接口,何时使用高阶函数?,dependency-injection,f#,functional-programming,Dependency Injection,F#,Functional Programming,给定具有以下层的ASP.NET MVC应用程序: 用户界面(视图、CSS、Javascript等) 控制器 服务(包含业务逻辑和数据访问) 之所以没有单独的数据访问层,是因为我使用的是SQL类型提供程序 (以下代码可能不起作用,因为它只是一个草稿)。 现在想象一个名为UserService的服务,其定义如下: module UserService = let getAll memoize f = memoize(fun _ -> f) let tryG

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

  • 用户界面(视图、CSS、Javascript等)
  • 控制器
  • 服务(包含业务逻辑和数据访问)
之所以没有单独的数据访问层,是因为我使用的是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
然后在我的控制器层中,我将有另一个名为
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 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
显然,接口实现的代码要少得多,但它不再像功能代码了。如果我开始在我的web应用程序中使用接口,我什么时候知道什么时候应该改用高阶函数否则,我将得到一个简单的面向对象解决方案


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

关于接口。首先,我认为您可以将接口看作是命名的函数对。如果您有:

type ICacheProvider =
  abstract Get : string -> option<obj>
  abstract Set : string * obj -> unit
…然后定义一个类型来包装它并添加缓存(这将由web应用程序的主类型使用-它与您在示例中定义的类型非常相似,但现在我们使用缓存提供程序分离数据库访问和存储):


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

我在我的博客上写了一些想法,试图澄清对象和高阶函数之间的关系,除此之外:单元测试框架也倾向于更好地模拟接口,而不是模拟函数签名的元组。
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
type ICacheProvider =
  abstract Get : string -> option<obj>
  abstract Set : string * obj -> unit
type CacheProvider = (string -> option<obj>) * (string * obj -> unit)
module UserService =
    let getAll () = (...)
    let tryGetByID id = (...)
    let add name = (...)
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