Biorąc aplikacji ASP.NET MVC z następujących warstw:Kiedy korzystać z interfejsów i kiedy korzystać z funkcji wyższego rzędu?
- UI (widoki, CSS, JavaScript, itp)
- Sterowniki
- usług (Zawiera logiki biznesowej i dostępu do danych)
Powodem braku oddzielnej warstwy dostępu do danych jest to, że korzystam z dostawcy typu SQL.
(Poniższy kod może nie działać, ponieważ jest to tylko wersja robocza). Teraz wyobraź sobie usługę o nazwie UserService
zdefiniowane następująco:
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
a potem w moim warstwy kontrolerów, będę mieć inny moduł nazwany UserImpl
lub może to być równie dobrze nazwana 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
użycie tego byłoby jak:
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
Korzystanie z interfejsów, mielibyśmy następujące wdrożenie:
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
I wykorzystanie byłoby coś jak:
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
Oczywiście realizacja interfejs ma znacznie mniej kodu, ale to naprawdę nie ma ochoty kodu funkcjonalnej więcej. Jeśli zacznę korzystać z interfejsów w całej mojej aplikacji internetowej, kiedy będę wiedzieć, kiedy użyć funkcji wyższego rzędu? - W przeciwnym razie skończę z prostym starym rozwiązaniem OOP.
Krótko mówiąc: Kiedy należy używać interfejsów i kiedy używać funkcji wyższego rzędu? - trzeba narysować jakąś linię, albo wszystkie będą typami i interfejsami, z których zniknie piękno FP.
Napisałem o tym kilka przemyśleń na moim blogu, próbując wyjaśnij związek między obiektami i funkcjami wyższego rzędu między innymi: http://bugsquash.blogspot.com/2013/08/objects-and-functional-programming.html –