Учитывая приложение ASP.NET MVC со следующими слоями:
- UI (Представления, 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 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
Используя интерфейсы, мы будем иметь следующую реализацию:
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
Очевидно, что в реализации интерфейса гораздо меньше кода, но он больше не похож на функциональный код. Если я начну использовать интерфейсы в своем веб-приложении, когда я узнаю, когда использовать функции более высокого порядка? - иначе я просто окажусь простым старым решением ООП.
Итак, вкратце: когда следует использовать интерфейсы и когда использовать функции более высокого порядка? - какая-то линия должна быть нарисована, или все это будут типы и интерфейсы, из которых исчезает красота FP.