2012-11-27 16 views
6

Perché io sono relativamente nuovo alla OOP/C# non so il modello giusto per risolvere questo:Object-oriented modello di progettazione per evitare if/then/else

devo costruire un'architettura a plugin per diversi fornitori di IO. L'host legge il nome/tipo del provider necessario dalla configurazione, quindi dovrebbe stabilire l'istanza e la parametrizzazione del provider.

così ho fondamentalmente queste interfacce:

public interface IoProvider //(Base interface, all Providers implements this) 
{ 
    void Initialize(); 
    void Execute(); 
} 

public interface IFileProvider: IoProvider 
{ 
    string PropertyA { get; set; } 
} 

public interface ISmtpProvider : IoProvider 
{ 
    string PropertyB { get; set; } 
    string PropertyC { get; set; } 
} 

come vedete i derivati, fornitori specializzati IO hanno diverse proprietà aggiuntive dei parametri che l'interfaccia di base non ha. Per evitare if/then/else o cambiare le istruzioni la mia idea era di usare uno schema di fabbrica.

Ma se lo capisco correttamente, non risolve il problema if/then/else perché sul client devo controllare il tipo derivato per fornire i parametri corretti.

Così il flusso del programma nel sistema host sarebbe qualcosa di simile: Host legge config, prende nome/tipo di necessaria Provider Host chiama Fabbrica e ottiene il provider

Ma come evitare questo - c'è un motivo per risolvere questo senza if/then/else?

If (provider == typeOf(IFileProvider)) 
PropertyA = value 
else if (provider == typeOf(ISmtpProvider)) 
PropertyB = value 
PropertyC = value 
Elseif … 
+0

cosa c'è di sbagliato con 'se/else'? –

+0

Mi piacciono le mappe per questo tipo di cose, ma le fabbriche spesso hanno grandi istruzioni sugli interruttori. Perché senti il ​​bisogno di rimuoverlo? –

+2

ho imparato che questo è uno stile di codifica errato. non ci sono solo 2 tipi di fornitori, al momento ci sono 5 fornitori diversi. – Radioactive

risposta

15

È possibile sostituire switch dichiarazione con il polimorfismo. Basta consentire a ciascun provider di configurare se stesso dalla configurazione. Questa è un'opzione migliore, perché ogni fornitore sa quali valori da cercare:

provider.Configure(); 

Questo metodo dovrebbe esistere in interfaccia di base:

public interface IoProvider 
{ 
    void Initialize(); 
    void Execute(); 
    void Configure(); 
} 

E ogni fornitore implementarlo:

public interface ISmtpProvider : IoProvider 
{ 
    string PropertyB { get; set; } 
    string PropertyC { get; set; } 

    public void Configure() 
    { 
     PropertyB = ConfigurationManager.AppSettins["B"]; 
     PropertyB = ConfigurationManager.AppSettins["C"]; 
    } 
} 

Il principale vantaggio di questo approccio è che avrete solo un posto dove cambiare, quando un nuovo fornitore aggiungerà alla vostra applicazione - basta aggiungere una nuova classe di provider, che sa come configurarsi. Non è necessario modificare anche l'implementazione dell'host. E il tuo host soddisferà il principio di OCP - aperto per l'estensione (puoi aggiungere nuovi provider), ma chiuso per la modifica (non è necessario modificare il codice esistente quando il nuovo fornitore ha aggiunto).

Inoltre è possibile passare alcuni oggetti di configurazione a Configure(IConfiguration config) con questo metodo (renderà il vostro codice verificabile e non dipendente dallo statico ConfigurationManager).

+0

sì, questo significa che l'host deve avere tutti i parametri per il plugin. Ma in realtà l'Host è esso stesso un (altro) plugin. Quindi quando implemento un nuovo IoProvider con nuovi parametri devo riscrivere (estendere con i nuovi parametri) tutti i plugin Host. – Radioactive

+4

Si potrebbe prendere in considerazione il passaggio di qualche oggetto di configurazione a tale metodo, al fine di evitare la dipendenza dalle impostazioni globali. Sarebbe più facile testare, penserei. Se vuoi ancora utilizzare le impostazioni globali, puoi farlo anche tramite qualcosa come provider.configure (ConfigurationManager.AppSettings); '. – cHao

+0

@cHao d'accordo, passare l'oggetto di configurazione è una buona soluzione. Anche in questo caso i test unitari saranno semplici. –

0

Se sei pronto a sbarazzarti delle istruzioni if ​​/ else, allora potresti avere un caricatore nella tua classe base, in cui passerai tutti i parametri, e poi ogni classe lo sovraccaricherà, e solo usa i parametri di cui hanno bisogno.

Vorrei dire che è un patter desiderabile solo se tutte le classi derivate sono garantite per utilizzare la stessa piccola raccolta di parametri, solo in modi diversi. In caso contrario, le dichiarazioni if/else sono probabilmente preferibili

-2

Alla fine si finisce sempre per dover usare una dichiarazione condizionale.

Certo, è possibile utilizzare alcune librerie per astrarre questo. Come una libreria di dipendenze per le dipendenze e basata su alcuni costruttori, potresti essere in grado di farlo senza se.

Ma ...

sua davvero non vale la pena, si aggiungerà un sacco di complessità al codice solo per renderlo più elegante e il compromesso di solito non vale la pena.

Questo non significa che non si possa fare qualcosa di meglio. Sparando dal fianco, aggiungerei un metodo come questo all'interfaccia.

vuoto init (parametri Dictionary), e in ogni implementazione di leggere dal dizionario e inizializzare le proprietà necessarie

+0

Normalmente posso vivere con dichiarazioni condizionali ma nel mio caso devo riscrivere gli host. (Host è esso stesso un plugin - leggi sopra nei miei commenti) – Radioactive

Problemi correlati