2009-11-10 2 views
24

Quali sono i diversi metodi per iniettare preoccupazioni trasversali in una classe in modo da ridurre al minimo l'accoppiamento delle classi coinvolte mantenendo il codice testabile (TDD o altrimenti)?Quali sono i diversi metodi per iniettare preoccupazioni trasversali?

Ad esempio, considerare se ho una classe che richiede sia la funzionalità di registrazione che la gestione centralizzata delle eccezioni. Dovrei usare DIP e iniettare entrambi i dubbi richiesti tramite un'interfaccia nella classe che li richiede? Dovrei usare un localizzatore di servizio che passo ad ogni classe che richiederà alcune funzionalità trasversali? C'è una soluzione diversa del tutto? Sto facendo la domanda sbagliata del tutto?

risposta

23

Il modello di progettazione Decorator è un eccellente punto di partenza per implementare preoccupazioni trasversali.

Per prima cosa è necessario definire un'interfaccia che modella il servizio in questione. Quindi implementate la reale funzionalità di quel servizio senza pensare alla vostra preoccupazione trasversale a tutti.

Quindi è possibile implementare successivamente le classi di decorazione che avvolgono altre istanze e implementano la preoccupazione trasversale desiderata.

Questo approccio può essere implementato interamente con oggetti Plain Old C# (POCO) e non richiede alcun framework aggiuntivo.

Tuttavia, se ti stanchi di scrivere tutti i decoratori in più, potresti voler utilizzare un quadro. Non ho esperienza con i framework AOP espliciti, ma la maggior parte dei DI contenitori come Castle Windsor offrono funzionalità simili a AOP.


Ecco un esempio di utilizzo di Decoratori. Diciamo che si ha la seguente interfaccia:

public interface IMyInterface 
{ 
    void DoStuff(string s); 
} 

L'implementazione concreta può fare qualcosa di molto interessante, come la scrittura della stringa alla console:

public class ConsoleThing : IMyInterface 
{ 
    public void DoStuff(string s) 
    { 
     Console.WriteLine(s); 
    } 
} 

Se si desidera registrare l'operazione DoStuff, ora è possibile implementare un decoratore di registrazione:

public class LoggingThing : IMyInterface 
{ 
    private readonly IMyInterface innerThing; 

    public LoggingThing(IMyInterface innerThing) 
    { 
     this.innerThing = innerThing; 
    } 

    public void DoStuff(string s) 
    { 
     this.innerThing.DoStuff(s); 
     Log.Write("DoStuff", s); 
    } 
} 

È possibile continuare a scrivere nuovi decoratori, come un decoratore di caching o uno che implementa la sicurezza e così via, e solo wr li apri a vicenda.

Nota: raramente raccomando le interfacce statiche, quindi l'interfaccia Log.Write non è una raccomandazione, ma indica semplicemente un segnaposto. In una vera implementazione, inserivo una sorta di interfaccia di ILogger in LoggingThing.

+0

Ho considerato anche il decoratore.Suppongo che se mantengo i miei metodi abbastanza concisi, non sacrificherò la granularità del controllo che potrei aspettarmi dalle soluzioni esemplificative che ho proposto. C'è qualche motivo per cui preferiresti Decorator? Perché la classe decorata non ha bisogno di conoscenza delle preoccupazioni trasversali? –

+0

Questa è la soluzione che preferisci per i problemi trasversali quando li gestisci? –

+0

Preferisco Decorator rispetto all'iniezione di strategie perché mantiene la vera implementazione squeky clean. Spesso lo preferisco, ma a volte può anche usare l'intercettazione come previsto ad es. Castello Windsor. –

4

È possibile utilizzare il modello Observer.

Il soggetto detiene una raccolta di osservatori. Quando il Soggetto esegue un'azione, notifica agli Osservatori il cambiamento. Gli osservatori possono quindi eseguire un'azione senza che il soggetto si interessi di quale sia l'azione.

Ecco un esempio:

public interface IThingObserver 
{ 
    void Notify(); // can also pass in a parameter with event information 
} 

public class Thing 
{ 
    private readonly ICollection<IThingObserver> observers; 

    public Thing() 
    { 
     observers = new List<IThingObserver>(); 
    } 

    public void RegisterObserver(IThingObserver observer) 
    { 
     observers.Add(observer); 
    } 

    public void UnregisterObserver(IThingObserver observer) 
    { 
     observers.Remove(observer); 
    } 

    private void NotifyObservers() 
    { 
     foreach (IThingObserver observer in observers) 
     { 
      observer.Notify(); 
     } 
    } 

    public void DoIt() 
    { 
     Console.WriteLine("Doing it..."); 
     NotifyObservers(); 
    } 
} 

public class LoggingThingObserver : IThingObserver 
{ 
    public void Notify() 
    { 
     Log.Write("It is done."); 
    } 
} 

Sono abituato a Java, quindi ti prego di perdonare eventuali errori di sintassi.

+0

So che l'ho fatto in origine nel '09, ma grazie per la risposta! L'osservatore è una buona raccomandazione che aiuta davvero a separare le preoccupazioni e colpisce bene con l'OCP. –