2010-03-29 7 views
7

Ich habe die folgenden zwei Methoden, die (wie man sieht) in den meisten seiner Aussagen ähnlich sind mit einer Ausnahme (Details siehe unten)Umgestalten die beiden folgenden C++ Methoden doppelten Code, um aus

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

Gibt es irgendeine Technik, die ich verwenden kann, um die gemeinsamen Codezeilen der beiden Methoden auf eine separate Methode zu verschieben, die aus den beiden Varianten OR aufgerufen werden könnte - möglicherweise die beiden Methoden zu einer einzigen Methode kombinieren?

Nachfolgend sind die Einschränkungen:

  1. Die Klassen SURFACE und CURVE sind von 3rd-Party-Bibliotheken und damit als nicht änderbar. (Wenn es hilft sie sowohl von IDispatch abgeleitet)
  2. Es gibt noch weitere ähnliche Klassen (zB FACE), die in diese „Vorlage“ passen könnte (nicht C++ Vorlage, nur der Fluss von Codezeilen)

ich folgendes wissen könnte (vielleicht?) als Lösungen implementiert werden, aber wirklich bin der Hoffnung, es eine bessere Lösung ist:

  1. ich einen dritten Parameter zu den 2 Methoden hinzufügen könnte - zB eine Enumeration - die den ersten Parameter identifiziert (zB enum :: input_type_surface, enum :: input_type_curve)
  2. Ich könnte einen IDispatch übergeben und dynamic_cast <> ausprobieren und testen, welche Besetzung NON_NULL ist und ein if-else, um das Recht aufzurufen Verfahren (zB getPlaneParams() vs. get_LineParams())

Das folgende ist keine Einschränkung, sondern wäre eine Forderung wegen meines Mitspielers Widerstand sein:

  1. nicht eine neue Klasse implementieren, die von der Oberfläche erbt/CURVE usw. (Sie würden es viel lieber lösen, wenn sie die Enum-Lösung verwenden, die ich oben angegeben habe)
+1

Sie nicht 'Params' Vektor löschen. Beabsichtigen Sie, es mit Parametern aus vielen Objekten zu füllen? Vielleicht gibt es viel bessere Möglichkeiten, Ihren Code umzuformen, je nachdem, was Sie vor dem Aufruf von geXXXXParameters-Methoden tun. –

+0

Warum wird ein 'unsigned int' zurückgegeben, wenn ein' bool' genügt? –

+0

Was ist der Typ von 'SafeDoubleArray'? Ich vermute, das könnte mehr refaktoriert werden, aber wir brauchen das zuerst. Ich zweite @ Matthieu Bewegung für eine 'bool'. – GManNickG

Antwort

10

Ein paar Ideen in den Sinn kommen, aber hier ist, was ich denke, am besten wäre:

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

Was Sie tun, ist die Aufgabe des Erhaltens Parameter an eine andere Funktion delegieren, die überlastet ist. Fügen Sie einfach Funktionen wie diese für jeden anderen Typ hinzu, den Sie haben. (Hinweis, ich habe Ihre Rückgabebefehle vereinfacht.)

+0

Schön. Ich mag das. Bin darauf gelehnt. Traurig habe ich selbst nicht daran gedacht. – ossandcad

+1

Sehr schön, besonders wenn man es mit der von Carl Manaster erwähnten 'Extract Method' kombiniert, um etwas Code (nach dem Aufruf von' detail :: getParameters') zu verschieben. Ich mag es besser, wenn meine Template-Methoden leicht sind: macht das Abhängigkeitsdiagramm leichter :) –

2

Warum nicht einfach die VARIANT varParams als Parameter an die Funktion anstelle einer CURVE oder SURFACE übergeben?

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

Sie können auch (wenn möglich) machen diese Methoden Vorlagen prüfen und einen output_iterator als Parameter anstelle eines vector erhalten. Auf diese Weise hängt Ihr Code nicht von der Art der verwendeten Sammlung ab.

+0

Wie würde das helfen? – Dave

+0

Es tut mir leid. Ich habe nicht ganz verstanden, was Sie vorschlagen. Die Methoden, die mit 'Oberfläche' und 'Kurve' bezeichnet werden, sind unterschiedlich - das ist die Codezeile, die zwischen den beiden Methoden unterschiedlich ist. Und VarParams ist eine lokale Variable, die später nicht benötigt wird. Vielleicht muss ich meine Frage neu formulieren, wenn sie nicht richtig gestellt wird? Könnten Sie vielleicht einen Beispielcode zur Verfügung stellen, der mir das Verständnis erleichtert? – ossandcad

+0

Hinzugefügt Code für Klarheit. Diese Lösung würde es kürzer machen, besonders wenn mehr Methoden betroffen sind. –

5

Extract Method. Alles nach den Zeilen, die Sie als anders markiert haben, ist identisch - extrahieren Sie es als eine Methode, die von beiden Ihrer ursprünglichen Methoden aufgerufen wird.

+0

Wahr - so viel gemeinsamer Code wie möglich zu einer separaten Methode. Das funktioniert. Aber heißt das, wenn ich mehr gemeinsamen Code vor der "anderen Codezeile" habe, dann wäre das "Extrahieren" schwierig? – ossandcad

+0

Grundsätzlich kann jeder Codeblock, der dupliziert wird, in seine eigene Methode extrahiert werden, unabhängig davon, wie viele Zeilen zu ihm führen oder ihm folgen. Es kann unordentlich werden, wenn die extrahierte Methode beispielsweise zu viele Parameter erfordert und alternative Refactorings manchmal geeigneter sind. Aber Extrakt Methode ist ein guter Anfang. –

+0

Sie müssten die Methode extrahieren zweimal (oder mehr, abhängig davon, wie viele Stellen Sie nicht identischen Code haben). Ja, es könnte zu diesem Zeitpunkt schwierig werden. –

0

Anstatt eine OBERFLÄCHE oder KURVE zu übergeben, übergeben Sie einen Verweis auf die Basisklasse und einen Methodenfunktionszeiger.Dann würde der Aufruf von surface-> getLine_parameters oder curve-> getPlaneParamters ersetzt durch (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

Makro! Normalerweise nicht die beste Lösung (es ist ein Makro) und wahrscheinlich nicht die beste in diesem, aber es wird den Job machen.

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! Bitte nicht die abgehört Anweisung:

return params.size() != 0; 

Es gibt (-1) (alle angehoben Bits) seit params leer ist, nicht (1).

Aber man kann wirklich die Rückkehr Anweisung wie folgt konsolidieren:

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

Es scheint, das Richtige für die Klassen SURFACE und CURVE von der abstrakten Klasse abgeleitet werden FIGURE oder SHAPE oder jemand mehr. So können Sie aus der Vorlage zu entkommen und die virtuelle überschreiben:

Verwandte Themen