IoC come localizzatore di servizi

4

Mi sono chiesto per un po 'di tempo su alcuni problemi durante l'utilizzo di Injection dipendenza:

In un'applicazione a livelli, di solito inserisco i repository nel servizio dell'applicazione utilizzando l'iniezione del costruttore:

public class SomeService
{
    private IRepositoryA _repoA;
    private IRepositoryB _repoB;
    private IRepositoryC _repoC;

    public SomeService(IRepositoryA repositoryA, 
                       IRepositoryB repositoryB, 
                       IRepositoryC repositoryC
                       /* Other dependencies*/)
    {
        _repoA = repositoryA;
        _repoB = repositoryB;
        _repoC = repositoryC;
    }

    public void SomeMethod1()
    {
        //Do something with repo A and B
    }

    public void SomeMethod2()
    {
        //Do something with repo C
    }

    //Other methods
}

I problemi con questo sono:

  • Quando il servizio diventa complicato, il numero di dipendenze aumenterà sempre di più e presto il costruttore diventerà brutto con molti parametri. Potrebbe essere che il servizio stia facendo troppe cose (violare SRP) e sia meglio dividerlo in più classi, ma penso che questa situazione non sia rara quindi sono curioso di sapere come gli altri risolvono questo

  • SomeMethod1 () utilizza solo repo A e B, ma quando il client lo chiama, il servizio viene creato con tutti i repository, il che significa che ha più dipendenze del necessario.

Per risolvere questi due problemi, vedo che alcune persone hanno un'altra astrazione, che è una fabbrica di repository

public interface IRepositoryFactory
{
    T GetRepository<T>() where T : IRepository;
}

public class RepositoryFactory
{
    public T GetRepository<T>()
    {
        //Use IoC to return correct IRepository
    }
}

e inserisci la fabbrica nella classe di servizio:

public class SomeService
{
    private IRepositoryFactory _factory;
    public SomeService(IRepositoryFactory factory)
    {
        _factory = factory;
    }

    public void SomeMethod1()
    {
        IRepositoryA repoA = _factory.GetRepository<IRepositoryA>();
        IRepositoryB repoB = _factory.GetRepository<IRepositoryB>();
        //Use repo A and B
    }

    public void SomeMethod2()
    {
        IRepositoryC repoC = _factory.GetRepository<IRepositoryC>();
        //Use repo C
    }

    //Other methods
}

Questo risolve i 2 problemi di cui sopra, ma ci sono alcuni altri problemi:

  • RepositoryFactory utilizza IoC come un servizio di localizzazione, considerato da molti come anti-pattern
  • RepositoryFactory ora ha una dipendenza dal framework IoC, che potrebbe essere indesiderabile nell'integrazione delle dipendenze (perché, come ho capito, l'applicazione dovrebbe avere il minimo di comprensione sull'IoC come possono, tranne per la composizione root, che è in cima a l'applicazione)

Quindi questo è un buon approccio da usare?

    
posta Phuong Nguyen 10.10.2014 - 03:09
fonte

2 risposte

4

Buona domanda!

When the service becomes complicated, the number of dependencies will grow more and more

In questo caso le tue dipendenze potrebbero non essere dipendenze autonome ma possono essere raggruppate in semplici classi di dipendenza che possono essere utilizzate per passare al costruttore.

Un buon segno sarebbe se più di una delle tue classi usa sia IRepositoryA an IRepositoryB nel costruttore e anche in alcuni dei loro metodi come nel tuo metodo SomeMethod1 . Dovresti riuscire a trovare un buon nome per la classe che raggruppa IRepositoryA e IRepositoryB . Altrimenti, considera di dirci il vero esempio in modo da poter analizzare meglio.

SomeMethod1() only uses repo A and B, but when client calls it, the service is created with all repositories, meaning it has more dependencies than it needs.

Ma va bene, perché normalmente si vuole essere sicuri che al momento della creazione della classe non si voglia più modificare gli IRepositories. Dovrebbero essere dichiarati come immutabili. Questa è una buona pratica.

Se non è questo il caso e devono essere mutabili, allora potresti fare in modo che la dipendenza di IRepositoryC sia passata a SomeMethod2 come parametro. La correzione / applicazione parziale può aiutarti a prevenire la duplicazione del codice qui quando chiami il metodo molte volte con lo stesso argomento IRepositoryC .

Un approccio diverso potrebbe passare tipi di opzioni nel costruttore. La tua classe avrebbe comunque la responsabilità di verificare i propri metodi se una determinata percentuale diIRepository è già stata fornita o è ancora vuota al momento della chiamata al metodo.

Quindi dipende un po 'da quello che vuoi veramente fare. Non c'è nessuna risposta generale a questo problema.

    
risposta data 10.10.2014 - 08:47
fonte
3

Il problema principale con il secondo approccio delle fabbriche di iniezione è che rimuove uno dei vantaggi forniti dall'iniezione IoC / dipendenza: non si sa più da quali repository dipende la classe senza scavare nel codice.

Sebbene questo non sia "cattivo" come la creazione dei repository stessi, la classe deve ancora sapere come richiederli da una fabbrica. Inoltre, hai aggiunto ulteriore complessità introducendo la fabbrica stessa.

Probabilmente starai meglio seguendo il tuo istinto che la classe sta facendo molto e separandolo in parti più piccole di responsabilità.

Sono completamente d'accordo con la risposta fornita da @valenterry è come la maggior parte delle cose, ymmv e dipende davvero da cosa vuoi fare. Il secondo approccio non è esattamente malvagio ed è perfettamente utile se questo è ciò che si finisce con.

    
risposta data 10.10.2014 - 13:16
fonte