2010-03-29 5 views
7

que tienen los dos siguientes métodos que (como se puede ver) son similares en la mayoría de sus estados a excepción de uno (ver más abajo para más detalles)Refactor los siguientes dos C++ métodos para mover a cabo código duplicado

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; 
} 

¿Hay alguna técnica que pueda usar para mover las líneas comunes de código de los dos métodos a un método separado, que podría llamarse desde las dos variaciones, O bien, posiblemente combinar los dos métodos con un solo método?

Las siguientes son las restricciones:

  1. las clases de superficie y CURVA son de bibliotecas 3 ª parte y, por tanto, no se puede modificar. (Si se ayuda a que ambos se derivan de IDispatch)
  2. Hay incluso clases más similares (por ejemplo FACE) que podría entrar en esta "plantilla" (no C plantilla ++, sólo el flujo de líneas de código)

sé podría (posiblemente?) implementarse como soluciones al siguiente, pero estoy realmente esperando que hay una solución mejor:

  1. podría añadir una tercera parámetro a los métodos 2 - por ejemplo, una enumeración que identifica el primer parámetro (por ejemplo, enum :: input_type_surface, enum :: input_type_curve)
  2. Podría pasar un IDispatch y probar dynamic_cast <> y probar qué conversión es NON_NULL y hacer un if-else para llamar a la derecha método (por ejemplo getPlaneParams() vs. get_LineParams())

La siguiente no es una restricción, pero sería un requisito debido a mi compañeros de resistencia:

  1. no implementa una nueva clase que hereda de SUPERFICIE/CURVE, etc. (Prefieren resolverlo usando la solución enum que indiqué anteriormente)
+1

no lo hace claro 'vector params'. ¿Pretendes llenarlo con parámetros de muchos objetos? Quizás haya formas mucho mejores de refactorizar su código según lo que haga antes de llamar a los métodos geXXXXParameters. –

+0

¿Por qué devolver un 'unsigned int' cuando un' bool' sería suficiente? –

+0

¿Cuál es el tipo de 'SafeDoubleArray'? Sospecho que esto podría refactorizarse más, pero lo necesitamos primero. Yo segundo @ movimiento de Matthieu para un 'bool'. – GManNickG

Respuesta

10

Un par de ideas vienen a la mente, pero esto es lo que creo que sería mejor:

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; 
} 

Lo que se hace es delegar la tarea de conseguir los parámetros de alguna otra función que está sobrecargado. Simplemente agregue funciones como esa para cada tipo diferente que tenga. (Tenga en cuenta que simplifiqué su declaración de devolución.)

+0

Agradable. Me gusta esto. Estoy inclinado hacia esto. Triste, no pensé en esto yo mismo. – ossandcad

+1

Muy bueno, especialmente si se combina con el 'Método de extracción 'mencionado por Carl Manaster para mover algún código (después de la llamada a' detail :: getParameters'). Me gusta más cuando los métodos de mi plantilla son livianos: hace un gráfico de dependencia más claro :) –

2

¿Por qué no pasa el VARIANT varParams como parámetro a la función en lugar de 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); 
} 

También puede considerar (si es posible) hacer esas plantillas métodos y recibir una output_iterator como parámetro en lugar de un vector. De esta forma, su código no depende del tipo de colección utilizada.

+0

¿Cómo ayudaría eso? – Dave

+0

Lo siento. No entendí del todo qué es lo que estás sugiriendo. Los métodos llamados por 'superficie' y 'curva' son diferentes, que es la línea de código que es diferente entre los dos métodos. Y varParams es una variable local que no se necesita más tarde. Quizás deba volver a formular mi pregunta si no se la pregunta correctamente. ¿Podría proporcionar algún código de muestra para ayudarme a entender? – ossandcad

+0

Se agregó el código para mayor claridad. Esta solución lo haría más corto, especialmente si se afectan más métodos. –

5

Extract Method. Todo después de que las líneas que ha marcado como diferentes sean idénticas, por lo tanto, extráigalo como un método que se llama desde sus dos métodos originales.

+0

True: el código más común posible para un método diferente. Esto funciona. Pero, ¿eso significa que si tengo un código más común antes de la "línea de código diferente", entonces "extraerlo" sería difícil? – ossandcad

+0

Básicamente, cualquier bloque de código que se duplique se puede extraer en su propio método, independientemente de cuántas líneas conducen a él, o seguirlo. Puede volverse desordenado, si el método extraído requiere demasiados parámetros, por ejemplo, y las refactorizaciones alternativas a veces pueden ser más apropiadas. Pero el método Extract es un buen lugar para comenzar. –

+0

Tendría que hacer Extraer método dos veces (o más, dependiendo de cuántos lugares tiene un código no idéntico). Sí, podría ser difícil en ese punto. –

0

En lugar de pasar en una SUPERFICIE o CURVA, pase una referencia a la clase base, y también un puntero de función de método.Entonces la llamada a surface-> getLine_parameters o curve-> getPlaneParamters sería reemplazada por (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! Por lo general, no es la mejor solución (es un macro) y probablemente no sea la mejor en este caso, pero hará el trabajo.

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! Por favor, no utilice la instrucción pinchado:

return params.size() != 0; 

Devuelve (-1) (todos los bits planteadas) desde params está vacía, no (1).

Pero realmente se puede consolidar la instrucción de retorno de esta manera:

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

Parece adecuado para las clases SURFACE y CURVE a derivar de la clase abstracta FIGURE o SHAPE o alguien más. Para que pueda escapar de la plantilla y el uso de la anulación virtual:

void getParameters(const FIGURE& surface, VARIANT& varParams) 
Cuestiones relacionadas