In una soluzione interna a cui ho lavorato, non sono stato in grado di comprendere il vantaggio di come le interfacce sono spesso implementate nel progetto. Quale è il seguente:
- Vuoi fare operazioni CRUD su un database per un modello / tabella specifico? Crea un servizio di visualizzazione .
- Hai creato un servizio di visualizzazione? Crea un'interfaccia per questo .
- Visualizza il servizio gestisce la maggior parte della logica.
- Tale logica in realtà deve accedere al database? Crea un repository.
- Crea un repository? Crea un'interfaccia per questo .
- Il repository utilizza l'iniezione delle dipendenze per stabilire un oggetto di contesto del database sovrastante.
Ecco un esempio e poi condividerò ciò che mi hanno detto gli sviluppatori originali (e perché non capisco il ragionamento):
ILicenseViewService
public interface ILicenseViewService
{
Task AssignLicenseAsync( ... );
Task CreateLicenseAsync( ... );
Task DeleteLicenseAsync( ... );
Task<License> GetLicenseAsync( ... );
...
}
LicenseViewService
public class LicenseViewService : ILicenseViewService
{
private readonly ILicenseRepository licenseRepository_;
public LicenseViewService(ILicenseRepository licenseRepository)
{
licenseRepository_ = licenseRepository;
}
public async Task AssignLicenseAsync( ... )
{
if (DateTime.UtcNow > license.ExpirationDate)
{
throw new Exception("License is expired.");
}
await licenseRepository_.AssignLicenseAsync( ... );
}
}
ILicenseRepository
public interface ILicenseRepository
{
Task AssignLicenseAsync( ... );
Task RevokeLicenseAsync( ... );
void DeleteLicense( ... );
}
LicenseRepository
public class LicenseRepository : ILicenseRepository
{
private readonly ApplicationDbContext context_;
public LicenseRepository(ApplicationDbContext context)
{
context_ = context;
}
public async Task AssignLicenseAsync( ... )
{
...
await context_.SaveChangesAsync();
}
}
A quanto ho capito, interfacce fornisce ai programmatori numerosi vantaggi come il sollievo di non preoccuparsi delle classi derivate come Wolf e Monkey quando si inviano quegli oggetti in una routine che si aspetta IAnimal, quando voler imporre un contratto su come viene utilizzata una classe e quando si desidera utilizzare l'iniezione di dipendenza su oggetti unici.
Non ho visto quasi nulla di questo nell'uso di queste interfacce CRUD. Nessun oggetto ereditato, nessuna classe derivata e nessuna necessità di imporre un contratto. Uno sviluppatore ha spiegato che l'intenzione era l'iniezione di dipendenza, in cui ho chiesto perché qualcosa del tipo:
using (var myLicenseDbContext = new LicenseRepository()) { }
o
using (var myLicenseDbContext = new LicenseViewService()) { }
non lo farebbe. L'argomento era che un programmatore avrebbe voluto essere in grado di scambiare una classe di simulazione e non avere un vero accesso al database. Un altro motivo era per quanto riguarda l'imbroglio dei test, nel qual caso è necessaria l'iniezione di dipendenza. Non ci sono altre soluzioni per raggiungerlo? Seguire questa ideologia progettuale significa che ogni classe del modello ha due interfacce, un servizio di visualizzazione e un repository per CRUD. Non è necessario?