Data un'applicazione ASP.NET MVC con i seguenti livelli:Quando utilizzare le interfacce e quando utilizzare le funzioni di ordine superiore?
- UI (Viste, CSS, JavaScript, ecc)
- Controllers
- Servizi (Contiene la logica di business, e l'accesso ai dati)
Il motivo per cui non esiste un livello di accesso ai dati separato, è che sto utilizzando provider di tipo SQL.
(Il seguente codice potrebbe non funzionare, in quanto è solo una bozza non elaborata). Ora immaginate un servizio denominato UserService
definito come:
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
E poi nel mio strato di Controller, avrò un altro modulo chiamato UserImpl
o potrebbe benissimo essere chiamato 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
Il l'uso di questo sarebbe come:
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
Utilizzando le interfacce, avremmo la seguente implementazione:
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
E l'uso sarebbe qualcosa di simile:
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
Ovviamente l'implementazione dell'interfaccia ha molto meno codice, ma in realtà non voglia codice funzionale più. Se inizio a utilizzare le interfacce in tutta la mia app Web, quando so quando utilizzare le funzioni di ordine superiore? - altrimenti finirò con una semplice vecchia soluzione OOP.
Quindi in breve: quando devono essere utilizzate le interfacce e quando utilizzare le funzioni di ordine superiore? - deve essere tracciata una linea, o saranno tutti i tipi e le interfacce, per cui la bellezza di FP scomparirà.
Ho scritto alcune riflessioni su questo sul mio blog, cercando di chiarire la relazione tra gli oggetti e le funzioni di ordine superiore tra le altre cose: http://bugsquash.blogspot.com/2013/08/objects-and-functional-programming.html –