But it forces the lower level abstraction to be defined inside the higher level.
No, non lo fa rispettare. L '"astrazione di livello inferiore" (l'interfaccia, come ILogger
nel tuo esempio) può essere definita al di fuori del componente "livello inferiore" e "livello superiore", può risiedere in una terza DLL indipendente che serve solo allo scopo di fornire le interfacce. Questo componente non fa parte né del componente di livello inferiore né di quello di livello superiore.
In realtà, ho scritto "può", perché non è l'unica alternativa possibile al design. Le altre due alternative (quella che hai suggerito: ILogger nel componente di livello superiore e quella suggerita da @KilianFoth, ILogger come componente del componente Logger), sono anche approcci validi, con il vantaggio che hai bisogno di meno DLL / componenti, ma anche alcuni inconvenienti relativi alle dipendenze o alle restrizioni risultanti sulla riusabilità. Mettere l'interfaccia ILogger
nella DLL Logger
impone che il tuo "componente di livello superiore" debba ancora dipendere dal componente Logger di livello inferiore, ovviamente, ma ti lascia comunque l'opzione di sostituire l'oggetto Logger
nel tuo componente di livello superiore da parte di un "logger di test" per scopi di test unitari, a patto che il codice del test non abbia il problema di avere una dipendenza dal link alla DLL del logger.
A lower level becomes dependent on the abstraction defined in the higher level and can't be used without it.
No, diventa dipendente dal componente dell'interfaccia. Non è possibile utilizzare il logger senza quel componente di interfaccia, ovviamente. Ma puoi riutilizzare la DLL del logger in combinazione con la DLL di ILogger in un progetto completamente separato.
In my example Adapter could implement ILogger interface from the higher-level code and reroute calls to the Logger class which now becomes reusable and independent from my application.
Lo scenario di utilizzo tipico di un adattatore è quando si hanno interfacce incompatibili e non si può facilmente modificare il codice esistente. Ad esempio, dove il componente di livello superiore BusinessCalculator
si aspetta un% co_de injected (un'interfaccia, forse definita nella DLL in cui BLogger
vive, con alcuni requisiti speciali). Il tuo componente BusinessCalculator
standard che hai già utilizzato in altri cinque progetti e per il quale non cambierai il design, si trova nella DLL "Logger riutilizzabile" e fornisce solo un'interfaccia Logger
incompatibile (forse definita direttamente nel tuo Logger DLL, forse definito in una interfaccia separata DLL). Quindi è necessaria una classe adattatore che esegue il mapping delle chiamate ILogger
a BLogger
.
Con quest'ultimo design, puoi in effetti separare completamente i componenti ILogger
e BusinessCalculator
, solo l'adattatore dipenderà dalle DLL in cui Logger
e BLogger
sono definite. Tecnicamente, non è nemmeno più chiaro quale dei due sia il componente più "di livello superiore" o "di livello inferiore". Lo svantaggio è il sovraccarico aggiuntivo per la gestione di tutte quelle interfacce e componenti diversi.