2011-12-21 10 views
5

Questa domanda è stata probabilmente pubblicata prima, ma non riuscivo a trovarla.Dichiarazione dell'interfaccia o dello switch, ricerca del modello corretto

Ho scritto questo genere di cose per così tanto tempo, mi siedo per scrivere qualcosa di nuovo e inizio a digitare questo come se fosse il mio schema. Di recente è uscito un progetto e mi sono ritrovato a guardare il mio codice e ho iniziato a pensare a quanto fosse puzzolente.

BackgroundInfoIfYouCare 

In questa particolare libreria ho bisogno di inviare e-mail agli utenti. Finora ci sono 13 e-mail in scatola.

Ogni e-mail ha il proprio modello (sto usando un parser Razor, quindi i modelli sono scritti in cshtml). Ogni modello di email ha un nome Chiave di stringa. Ogni e-mail ha la propria query EF4 per restituire un modello basato su un'entità "membership" e tutti i dati correlati.

Ho una classe che accetta una stringa che è una chiave nome modello di e-mail.

Il metodo eseguirà la query appropriata e recupererà un elenco, acquisendo il modello di email.

L'elenco e il modello vengono passati a un parser per unire ciascuna delle appartenenze al modello e restituisce un elenco di e-mail.

EndOfBackgroundInfoIfYouCare 

Quindi la vera domanda ... qual è il modo migliore per farlo?

Un modo è quello di utilizzare solo un interruttore

public List<Membership> Execute(string TemplateKey) { 
switch (TemplateKey) 
     { 
      case "SomethingExpired": 
       QueryResult = new SomethingExpiredEmailQuery().ExecuteQuery(); 
       break; 
      case "SomethingExpireIn30": 
       QueryResult = new SomethingExpireIn30EmailQuery().ExecuteQuery(); 
       break; 
      case "FirstTimeLoginThanks": 
       QueryResult = new FirstTimeLoginThanksEmailQuery().ExecuteQuery(); 
       break; 
      case "SecurityTraining": 
       QueryResult = new SecurityTrainingEmailQuery().ExecuteQuery(); 
       break; 
      case ETC ETC ETC... 

}

Un altro modo sarebbe quello di utilizzare un'interfaccia

IEmailQuery 
void ExecuteQuery() 

Ma se io uso un'interfaccia che sarà ancora bisogno di istanziare la classe Query. Non salva codice e non semplifica la manutenzione del codice.

Con riflessione avrei potuto fare qualcosa di simile a nome di tutte le query e-mail con un motivo: Email chiave modello di SecurityTraining ha un nome di query di SecurityTrainingEmailQuery e ho potuto utilizzare la reflection per creare un'istanza e chiamare il metodo ExecuteQuery.

Senza utilizzare la riflessione, non esiste un modo più pulito per collegarlo?

risposta

3

In realtà, questo non sembra troppo maleodorante per me. Se non ti piace l'istruzione switch potresti andare su IEmailQuery-Path e collegarlo in un Dictionary<string,IEmailQuery>. Questo probabilmente salva alcune righe di codice, come si poteva accedere in quel modo:

QueryDictionary["MyKey"].ExecuteQuery(); 

Cheers, Oliver

+0

Beh, la risposta di Jon è simile, solo il modo più avanzato del mio temo ;-) – Lindan

7

Un'opzione è di avere una mappa Dictionary<string, Func<IEmailQuery>>. Si potrebbe costruire in questo modo:

private static readonly Dictionary<string, Func<IEmailQuery>> MailQueryMap = 
    new Dictionary<string, Func<IEmailQuery>> { 
    { "SomethingExpired",() => new SomethingExpiredMailQuery() }, 
    { "SomethingExpireIn30",() => new SomethingExpireIn30EmailQuery() }, 
    // etc 
}; 

Poi:

public List<Membership> Execute(string templateKey) { 
    IEmailQuery query = MailQueryMap[templateKey].Invoke(); 
    var queryResult = query.ExecuteQuery(); 
    // ... 
} 

Se è possibile garanzia che è sempre e solo bisogno di costruttori senza parametri, si può sempre memorizzare un Dictionary<string, Type> e un'istanza attraverso la riflessione - ma ci saranno alcuni brutti lanci ecc.

EDIT: Naturalmente, se il nome del modello sempre è il nome del tipo, è possibile utilizzare

Type queryType = Type.GetType(namespacePrefix + "." + templateKey); 
IEmailQuery query = (IEmailQuery) Activator.CreateInstance(queryType); 
var queryResult = query.ExecuteQuery(); 

Si consiglia inoltre di considerare utilizzando un enum invece della magia costanti di stringa.

+0

modo questo segue il principio aperto/chiuso? Se leggo correttamente il post di Paul, vuole evitare di dover modificare le classi esistenti (estendendo l'istruzione switch con nuovi casi). – Wivani

+0

@Wivani: Non ho visto nulla che lo suggerisse: ho visto solo che desidera che il codice sia più semplice e facile da mantenere. Dove nella domanda parla di evitare di dover cambiare le classi esistenti? –

+0

Indovina che sto facendo un "asino con me" ;-) Vediamo se Paul può confermare quello che penso stia cercando. – Wivani

0

Perché non utilizzare il riflesso come proposto nella domanda? Penso che sia un modo valido per fare questo genere di cose.

Un altro approccio sarebbe utilizzare l'inversione del modello di iniezione controllo/dipendenza. Definite un'interfaccia come avete fatto e registrate tutte le implementazioni concrete conosciute nel vostro contenitore DI (ciò può essere fatto mediante configurazione o codice).

Al momento della registrazione, è necessario indicare al contenitore DI il nome di servizio per distinguere le implementazioni poiché implementano la stessa interfaccia.

YourIocContainer.Register<IEmailQuery>(typeof(SomethingExpiredMailQuery), 
             "SomethingExpiredMailQuery"); 

Quando un'istanza, è possibile ottenere l'applicazione corrispondente fornendo ancora una volta il nome del servizio:

public List<Membership> Execute(string TemplateKey) { 
    YourIocContainer.Resolve<IEmailQuery>(TemplateKey); 
+0

Quando stavo pensando di porre la domanda sullo stackoverflow, la prima cosa che ho pensato è stata la riflessione.La velocità non è un problema, quindi sarebbe un metodo valido da utilizzare. Ero solo curioso di sapere quali altre soluzioni potrebbero esserci là fuori a cui non avevo pensato. Mi piace anche la tua soluzione di iniezione di dipendenza. –

1

mi piacerebbe andare per un modello di fabbrica, qualcosa come

class EmailQueryFactory 
{ 
    public IEmailQuery Create(String TemplateKey) 
    { 
    .... 
    } 
} 

e poi

//.. first get String TemplateKey 

IEmailQuery qry=EmailQueryFactory.Create(TemplateKey); 
qry.Execute(); 
+0

Stavo pensando di usare una fabbrica, ma poi in fabbrica si finisce con lo stesso problema. È necessario collegare il modello richiesto alla classe Query corretta. –

0

Il pattern di comando è un pattern perfetto da utilizzare per questo scenario. Vedere http://www.codeproject.com/KB/books/DesignPatterns.aspx per una descrizione pratica # di questo modello. Lambdas, come hanno descritto Jon Skeet, sono utili nuovi costrutti di programmazione che puoi vedere. Vedi Command Pattern : How to pass parameters to a command? per ulteriori discussioni sull'uso del modello.

+0

Grazie per aver menzionato il modello di comando. Mi ha costretto a rileggere la definizione dal libro di 4gang. Sono curioso di sapere cosa guadagnerei con il suo uso. Non avrei ancora bisogno di creare un'istanza del ricevitore per invocare il comando? Mi riporta a un elenco di classi e associando un comando richiesto a quello corretto. –

Problemi correlati