2010-03-29 8 views
7

Ho le seguenti due metodi che (come potete vedere) sono simili nella maggior parte delle sue dichiarazioni ad eccezione di uno (vedi sotto per i dettagli)Refactor il seguente due ++ metodi C per spostare il codice duplicato

unsigned int CSWX::getLineParameters(const SURFACE & surface, vector<double> & params) 
{ 
    VARIANT varParams; 

    surface->getPlaneParams(varParams); // this is the line of code that is different 

    SafeDoubleArray sdParams(varParams); 

    for(int i = 0 ; i < sdParams.getSize() ; ++i) 
    { 
     params.push_back(sdParams[i]); 
    } 

    if(params.size() > 0) return 0; 
    return 1; 
} 

unsigned int CSWX::getPlaneParameters(const CURVE & curve, vector<double> & params) 
{ 
    VARIANT varParams; 

    curve->get_LineParams(varParams); // this is the line of code that is different 

    SafeDoubleArray sdParams(varParams); 

    for(int i = 0 ; i < sdParams.getSize() ; ++i) 
    { 
     params.push_back(sdParams[i]); 
    } 

    if(params.size() > 0) return 0; 
    return 1; 
} 

Esiste qualche tecnica che io possa usare per spostare le linee comuni di codice dei due metodi in un metodo separato, che potrebbe essere chiamato dalle due varianti - OPPURE - eventualmente combinare i due metodi in un unico metodo?

Di seguito sono riportati i limiti:

  1. Le classi di superficie e CURVA sono da librerie 3a parte e quindi non modificabile. (Se aiuta entrambi sono derivati ​​da IDispatch)
  2. ci sono anche le classi più simili (ad es viso) che potrebbero rientrare in questa "template" (non C modello ++, solo il flusso di linee di codice)

so quanto segue potrebbe (forse?) essere implementato come soluzioni, ma sono davvero sperando che ci sia una soluzione migliore:

  1. potrei aggiungere un terzo parametro per i 2 metodi - per esempio un enum - che identifica il 1 ° parametro (es. enum :: input_type_surface, enum :: input_type_curve)
  2. Posso passare in IDispatch e provare dynamic_cast <> e verificare quale cast è NON_NULL e fare un if-else per chiamare il diritto metodo (ad esempio getPlaneParams() vs. get_LineParams())

che segue non è una limitazione, ma sarebbe un requisito a causa della mia resistenza compagni di squadra:

  1. non implementa una nuova classe che eredita da SUPERFICIE/CURVE ecc. (Preferirebbero molto risolverlo usando la soluzione enum che ho affermato sopra)
+1

non si cancella il vettore 'params'. Hai intenzione di riempirlo con i parametri di molti oggetti? Forse ci sono modi migliori per rifattorizzare il tuo codice a seconda di ciò che fai prima di chiamare i metodi geXXXXParameters. –

+0

Perché restituire un 'int unsigned quando un' bool' è sufficiente? –

+0

Qual è il tipo di 'SafeDoubleArray'? Sospetto che questo possa essere ulteriormente rifatto, ma per prima cosa ne abbiamo bisogno. Io secondo la mozione di @ Matthieu per un 'bool'. – GManNickG

risposta

10

Un paio di idee vengono in mente, ma ecco quello che penso sarebbe meglio:

namespace detail 
{ 
    void getParameters(const SURFACE& surface, VARIANT& varParams) 
    { 
     surface->getPlaneParams(varParams); 
    } 

    void getParameters(const CURVE& curve, VARIANT& varParams) 
    { 
     curve->get_LineParams(varParams); 
    } 
} 

template <typename T> 
unsigned int getParameters(const T& curve, vector<double> & params) 
{ 
    VARIANT varParams; 
    detail::getParameters(curve, varParams); 

    SafeDoubleArray sdParams(varParams); 
    for(int i = 0 ; i < sdParams.getSize() ; ++i) 
    { 
     params.push_back(sdParams[i]); 
    } 

    return params.size() != 0; 
} 

quello che fai è delegato il compito di ottenere i parametri per qualche altra funzione che viene sovraccaricato. Basta aggiungere funzioni del genere per ogni tipo diverso che hai. (Nota, ho semplificato la dichiarazione di ritorno.)

+0

Bello. Mi piace questo. Mi sto appoggiando a questo. Triste non ci ho pensato io stesso. – ossandcad

+1

Molto bello, specialmente se combinato con il 'Metodo di estrazione 'menzionato da Carl Manaster per spostare del codice (dopo la chiamata a' detail :: getParameters'). Mi piace di più quando i miei metodi template sono chiari: crea un grafo di dipendenza più leggero :) –

2

Perché non passare semplicemente lo VARIANT varParams come parametro per la funzione anziché CURVE o SURFACE?

unsigned int CSWX::getParameters(VARIANT varParams, vector<double> & params) 
{ 
    SafeDoubleArray sdParams(varParams); 

    for(int i = 0 ; i < sdParams.getSize() ; ++i) 
    { 
     params.push_back(sdParams[i]); 
    } 

    if(params.size() > 0) return 0; 
    return 1; 
} 

unsigned int CSWX::getPlaneParameters(const CURVE & curve, vector<double> & params) 
{ 
    VARIANT varParams;  

    curve->get_LineParams(varParams); // this is the line of code that is different 

    return getParameters(varParams, params); 
} 

Si può anche prendere in considerazione (se possibile) fare quei modelli di metodi e ricevere una output_iterator come parametro invece di un vector. In questo modo il tuo codice non dipende dal tipo di collezione utilizzata.

+0

Come sarebbe di aiuto? – Dave

+0

Mi dispiace. Non ho ben capito cosa stai suggerendo. I metodi chiamati da 'superficie' e 'curva' sono diversi - che è la linea di codice che è diversa tra i due metodi. E varParams è una variabile locale che non è necessaria in seguito. Forse ho bisogno di riformulare la mia domanda se non correttamente richiesta? Potresti fornire qualche codice di esempio per aiutarmi a capire? – ossandcad

+0

Aggiunto codice per chiarezza. Questa soluzione lo renderebbe più breve, soprattutto se sono interessati più metodi. –

5

Extract Method. Tutto ciò che segue le linee contrassegnate come diverse è identico, quindi estrailo come metodo chiamato da entrambi i tuoi metodi originali.

+0

Vero: tutto il codice più comune possibile per un metodo separato. Questo funziona. Ma questo significa che se avessi codice più comune prima della "diversa linea di codice", allora "estrarre" sarebbe difficile? – ossandcad

+0

Fondamentalmente, qualsiasi blocco di codice duplicato può essere estratto nel suo metodo, indipendentemente dal numero di linee che lo portano o seguirlo. Può diventare caotico, se il metodo estratto richiede troppi parametri, ad esempio, e i refactoring alternativi a volte possono essere più appropriati. Ma il metodo di estrazione è un buon punto di partenza. –

+0

Dovresti eseguire il metodo di estrazione due volte (o più, a seconda di quanti posti hai codice non identico). Sì, potrebbe diventare difficile a quel punto. –

0

Invece di passare in SUPERFICIE o CURVA, passare un riferimento alla classe di base e anche un puntatore di funzione del metodo.Quindi la chiamata a superficie-> getLine_parameters o curve-> getPlaneParamters verrà sostituita da (shape -> * getParamters) (varParams);

typedef void Baseclass::*ParamGetter(VARIANT varParams); 

unsigned int CSWX::getLineParameters(const Baseclass & geometry, ParamGetter getParams 
     vector<double> & params) 
{ 
    VARIANT varParams; 

    (geometry->*getParams)(varParams); // this is the line of code that is different 

    SafeDoubleArray sdParams(varParams); 

    for(int i = 0 ; i < sdParams.getSize() ; ++i) 
    { 
     params.push_back(sdParams[i]); 
    } 

    if(params.size() > 0) return 0; 
    return 1; 
} 
-2

Macro! Di solito non è la soluzione migliore (è una macro) e probabilmente non è la migliore in questo, ma farà il lavoro.

macro_GetDakine_Params(func) 
    VARIANT varParams; \ 
    curve->##func(varParams); // this is the line of code that is different \ 
    SafeDoubleArray sdParams(varParams); \ 
    for(int i = 0 ; i < sdParams.getSize() ; ++i) \ 
    { \ 
     params.push_back(sdParams[i]); \ 
    } \ 
    if(params.size() > 0) return 0; \ 
    return 1; \ 

unsigned int CSWX::getPlaneParameters(const CURVE & curve, vector<double> & params) 
{ 
    macro_GetDakine_Params(getPlaneParams) 
} 
unsigned int CSWX::getLineParameters(const CURVE & curve, vector<double> & params) 
{ 
    macro_GetDakine_Params(getLineParams) 
} 
0

O! Per favore, non utilizzare l'istruzione spiati:

return params.size() != 0; 

Esso restituisce (-1) (tutti i bit in rilievo) dal params è vuoto, non è (1).

Ma davvero possibile consolidare l'istruzione return in questo modo:

return (params.size() != 0) ? 0 : 1; 

Sembra giusto per le classi SURFACE e CURVE essere derivata dalla classe astratta FIGURE o SHAPE o più qualcuno. Quindi è possibile uscire dal modello e utilizzare la sovrascrittura virtuale:

void getParameters(const FIGURE& surface, VARIANT& varParams) 
Problemi correlati