2009-08-24 12 views
7

Ho una domanda. Nel framework, che è stato scritto in gran parte prima che arrivassero i generici, si vede spesso una funzione con molti sovraccarichi per fare qualcosa con tipi diversi.Sovraccarico vs argomenti generici

una)

Parse(int data) 
Parse(long data) 
Parse(string data) 
..etc 

che sembra essere essere ok in quanto contribuisce a mantenere il codice piccola per ogni metodo e così. D'altra parte, ora con i generici è possibile effettuare le seguenti operazioni:

b)

Parse<T>(T data) 

e quindi avere un qualche tipo di IFS/istruzioni switch con typeof() per cercare di dedurre quali sono i tipi sono e cosa fare con loro.

Qual è la migliore pratica? O quali sono le ideologie che mi avrebbero aiutato a scegliere tra a) eb)?

+4

La procedura consigliata è: utilizzare _generics_ quando si implementa qualcosa che applica _generalmente_ a un numero arbitrario di tipi. Utilizzare l'overloading quando si ha _un numero specifico di alternative_. –

risposta

18

IMHO, se hai bisogno di istruzioni if ​​/ switch, dovresti sovraccaricare meglio. Generics dovrebbe essere usato dove l'implementazione non dipende dal tipo concreto per riutilizzarlo ancora.

Così come regola generale:

  • sovraccarico se ci sarà un'implementazione separato per ogni tipo
  • uso dei farmaci generici se si può avere una singola implementazione che funziona per tutti i tipi possibili.
+1

Potrebbe risultare un po 'sfocato, se ne esistono diversi tipi con un comportamento specifico e un comportamento predefinito per il resto dei tipi. –

+0

Certo, questa è una semplice regola per il gruppo di casi semplici, ma non è sempre così semplice. Se diventa complesso, si potrebbe considerare di implementare alcuni schemi, come strategia, visitatore o altro. –

+3

@John - Potresti avere sia 'Parse (int data)' (così come altri tipi specifici) e 'Analizza (T data)' per qualsiasi altra cosa. Se uno dei tipi specifici di overload viene passato, verrà utilizzato il metodo non generico, altrimenti l'argomento type verrà dedotto dal metodo generico. – statenjason

4

Il pattern che descrivi dove l'uso di generici risulta in un gruppo di istruzioni ifs/switch è un anti-pattern.

Una soluzione a questo è implementare un modello di strategia, che consente di utilizzare i generici, ma allo stesso tempo isolare le preoccupazioni del metodo Parse dal sapere come affrontare ogni caso differente.

Esempio:

class SomeParser 
{ 
    void Parse<T>(ParseStrategy<T> parseStrategy, T data) 
    { 
     //do some prep 

     parseStrategy.Parse(data); 

     //do something with the result; 
    } 
} 

class ParseStrategy<T> 
{ 
    abstract void Parse(T data); 
} 

class StringParser: ParseStrategy<String> 
{ 
    override void Parse(String data) 
    { 
     //do your String parsing. 
    } 
} 

class IntParser: ParseStrategy<int> 
{ 
    override void Parse(int data) 
    { 
     //do your int parsing. 
    } 
} 

//use it like so 
[Test] 
public void ParseTest() 
{ 
    var someParser = new SomeParser(); 
    someParser.Parse(new StringParser(), "WHAT WILL THIS PARSE TO"); 
} 

e allora si sarebbe in grado di passare in una qualsiasi delle strategie si sviluppano. Ciò ti consentirebbe di isolare correttamente i tuoi dubbi su più classi e non violare SRP (principio di responsabilità singola

1

Mentre non esiste una regola valida per tutti, i generici dovrebbero essere utilizzati quando il tipo specifico è irrilevante. Ciò non vuol dire che non sia possibile vincolare il tipo, ma quel tipo specifico non è rilevante, in questo caso il metodo di analisi dipende interamente (e diverso per) da ciascun tipo. sembrare una buona soluzione qui.

7

Codice odore.

Se si dispone di "un qualche tipo di se/interruttore ", questo è un odore di codice che solo il polimorfismo delle urla. Suggerisce che i generici sono non la soluzione a questo problema. Generics deve essere utilizzato quando il codice non dipende dai tipi di calcestruzzo che si passano in esso.

Guarda questo video di Google Tech Talks: "The Clean Code Talks -- Inheritance, Polymorphism, & Testing". Si rivolge specificamente a ciò di cui stai parlando.

+3

Yuck ... Poche cose sono più difficili da leggere rispetto al codice in cui il programmatore ha tentato di sostituire affermazioni perfettamente semplici e semplici con intere gerarchie di classi gonfiate. – jalf

+0

@jalf Yuck ... Poche cose sono più difficili da * mantain * di codice in cui semplici istruzioni if ​​che dipendono da molte opzioni vengono ripetute più e più volte :) Il polymorfism qui aiuta a mantenere il codice: una nuova opzione è solo una nuova classe e un nuovo metodo, non una ricerca completa attraverso tutto il codice nel progetto! –

+0

@ Danibishop oh, vedo che hai appena inventato la parte "ripetuta più e più volte" dal nulla. Purtroppo, non ha * * a che fare con il mio commento o la risposta che ho commentato. La risposta dice che * se hai qualche tipo di if/switch *, è un odore di codice. Non dice nulla sulla ripetizione. – jalf

2

Un problema qui - se stai richiedendo se le istruzioni/switch per far funzionare i generici, probabilmente hai un problema più grande. In questa situazione, è molto probabile che l'argomento generico non funzioni (correttamente) per OGNI tipo, solo un insieme fisso di tipi che stai gestendo. In tal caso, è molto meglio fornire sovraccarichi per gestire singolarmente i tipi specifici.

Questo ha molti vantaggi:

  • v'è alcuna possibilità di un uso improprio - siete utente non può passare un argomento non valido
  • C'è più chiarezza nella vostra API - è molto evidente che i tipi siano appropriati
  • metodi generici sono più complicate da usare, e non come ovvia per gli utenti che iniziano
  • L'uso di un generico suggerisce che qualsiasi tipo è valida - in realtà dovrebbe funzionare su tutti i tipi
  • Il metodo generico sarà probabilmente più lento, prestazioni saggio

Se il tuo argomento PUO funzionare con qualsiasi tipo, questo diventa meno chiaro. In tal caso, spesso considererei l'inclusione degli overload, oltre a un metodo di fallback generico per i tipi. Ciò fornisce un aumento delle prestazioni quando si passa un tipo "previsto" al metodo, ma è comunque possibile lavorare con altri tipi non previsti.

Problemi correlati