2009-05-06 18 views
4

Bei der Arbeit haben wir einen Legacy-Prozess in Visual C++ geschrieben, der im Grunde aus einer einzigen 5000-Zeilen-Funktion besteht. Im Wesentlichen ist das Programm nur eine große Fallanweisung mit ähnlichem Cut-and-Pasted-Code, der einen großen Teil der Logik für den Fall behandelt. Offensichtlich möchten wir diesen Code umgestalten, um diese Fälle in separate Funktionen (oder Objekte) zu extrahieren und jeglichen ausgeschnittenen und eingefügten Code zu entfernen.Refactoring einer großen C++ Funktion

Meine Frage ist - gibt es irgendwelche Vorschläge für eine Refactoring-Anstrengung dieser Größe? Gibt es automatisierte Tools, die den Prozess optimieren könnten?

Antwort

6

Mein erster Schritt wäre, einige der größeren Fälle zu nehmen und sie zuerst in separate Funktionen zu schieben. Das wird das visuelle Durcheinander für einen Anfang reduzieren und es Ihnen einfacher machen, die nächste Phase zu machen.

Zweitens, identifizieren Sie die Gemeinsamkeit der verschiedenen Fälle und erstellen Sie verallgemeinerte Funktionen, um an ihrer Stelle aufzurufen. Bis zu einem Punkt. Wenn du zu weit gehst, hast du eine verallgemeinerte Funktion, die genauso schlimm ist wie deine aktuelle switch-Anweisung :-)

Ich habe noch nie ein Werkzeug gesehen, das die Hälfte der Arbeit der schwammigen Sache in dir erledigen kann Schädel. Ich würde vorschlagen, das einfach zu benutzen.

+0

Viele IDEs geben Ihnen Werkzeuge, um damit zu helfen - Sie müssen nicht mehr alles kopieren/einfügen und mit den Parametern herumspielen. –

+0

@Andrei Krotkov, ja, aber sie wollen intelligente Entscheidungen darüber treffen, was in Funktionen umgewandelt werden soll. Sobald Sie jedoch einen Block finden, der ein guter Kandidat ist, sind diese Werkzeuge sehr üblich, um solche Refactorings einfach zu machen. – BobbyShaftoe

+0

Oh natürlich. Es gibt kein Werkzeug, um es magisch für dich zu tun - aber es gibt Werkzeuge, die helfen, von denen ich denke, dass die Frage war. –

0

Ich weiß Eclipse betont Refactoring als eine Funktion. Es gibt eine Auflistung aller nützlichen Funktionen auf IBM's website, aber besonders das "neue Methode aus Auswahl" Werkzeug scheint in Ihrem Fall anwendbar.

0

Dies ist eine Art von breiter Frage. Es gibt einige automatisierte Tools, aber in Wirklichkeit müssen Sie nur den Code studieren und einige Entscheidungen treffen. Gibt es Redundanz im Code? Wenn ja, dann denken Sie darüber nach, das überflüssige Zeug in seine eigenen Funktionen zu setzen.

3

Mehrere Vorschläge:

können Sie finden das Kopieren und Einfügen Code mit einem Tool wie Duplo. Ein Skript (in der Skriptsprache Ihrer Wahl) oder ein Editor mit mehrzeiligem Suchen und Ersetzen kann Ihnen helfen, diesen Code durch Funktionsaufrufe zu ersetzen. (Dies kann auch per Hand erfolgen, aber die Verwendung eines Skripts oder Suchen und Ersetzen hilft, Fehler auszuschließen.)

Refactoring-Tools können Operationen wie das Extrahieren eines Teils einer Funktion in eine neue Funktion automatisch ausführen. Eclipse CDT zum Beispiel kann dies tun, und es ist kostenlos, und es kann Code bearbeiten, der in anderen IDEs verwaltet wird. Das funktioniert nicht immer, aber wenn es funktioniert, ist es wirklich erstaunlich zuzusehen, wie die IDE eine Tausende-Zeilen-Methode aufteilt, genau das extrahiert, was Sie wollen, und jede Variable, die als neuer Parameter übergeben werden muss, korrekt identifiziert zu Ihrer neuen Methode ... Andere Refactoring-Tools sind verfügbar, wie Refactor! Pro (kostenlose Version verfügbar), aber ich habe sie nicht verwendet.

Allgemeiner, Michael Feathers Buch Working Effectively with Legacy Code ist die Standardarbeit, um diese Art von Sache zu machen. Grundsätzlich werden Sie Charakterisierungstests durchführen wollen - ähnlich wie bei Komponententests, aber das Ziel besteht darin, so viel wie möglich vom Verhalten der aktuellen Funktion abzudecken, anstatt die kleinsten möglichen Einheiten auf Korrektheit zu testen - dann wenden Sie Refactorings an zu einer Zeit. (Feathers enthält einen Katalog von Refactorings und anderen Techniken, die besonders nützlich für Legacy-Code sind.)

3

Der allererste Schritt ist, einen guten automatisierten Regressionstest zu entwickeln, wenn Sie noch keinen haben. Dann, wenn Sie jeden Fall zu einer Funktion herausziehen, können Sie schnell überprüfen, dass Sie nichts gebrochen haben.

+0

Das war mein erster Gedanke, habe einen Komponententest, um sicherzustellen, dass die Endfunktionalität die gleiche wie das Original ist. –

+0

Wo "gut" bedeutet, deckt die meisten oder idealerweise alle Pfade durch Ihre Funktion ab. Ein Coverage-Tool kann helfen, diese Qualität des Tests zu bewerten; Entschuldigung, ich kenne keine für C, aber ich bin mir sicher, dass sie verfügbar sind. –

+0

gcov funktioniert ganz gut für C. Die Abdeckung reicht jedoch nicht aus, um zu überprüfen, ob der Code funktioniert. Zusätzlich zum Testen des Codes sollten Sie ihn * unbedingt * verstehen, bevor Sie etwas berühren. Jede einzelne Zeile. – Tom

0
  1. Versuchen Sie nicht, alles auf einmal zu machen.
  2. identifizieren Sie ein wahrscheinliches Refactoring-Ziel. Sei so eng wie möglich.
  3. schreiben Sie Tests, um die korrekte Funktionalität dieses Codeabschnitts zu überprüfen.
  4. Sobald alle Ihre Tests bestanden haben, oder fehlschlagen, weil die ursprüngliche Funktion tatsächlich diesen Fehler hat, refaktorieren Sie dieses Bit.
  5. Stellen Sie sicher, dass alle Ihre Tests noch bestehen.
  6. GOTO 2.
2

versuchen, Visual AssistX bei www.wholetomato.com. Es integriert sich direkt in jede Version von Visual Studio ab VS6. Es enthält viele großartige Entwicklungsfunktionen, aber was Sie suchen, ist die Refactoring-Funktion. Sie können diese Funktion here sehen. Es kostet zwar, aber ich betrachte es als "Geheimwaffe" bei der Entwicklung mit Visual Studio.

0

Im Anschluss an, was Steve Fallows geschrieben hat, nachdem Sie einen Komponententest haben, um sicherzustellen, dass die Funktionalität sich ändert, erstellen Sie eine neue Funktion und haben die gleichen Komponententests wie das Original damit arbeiten.

Im Moment werden sie alle scheitern.

Dann fangen Sie an, jede case-Anweisung herauszunehmen und sie in ihre eigene Funktion zu setzen, und rufen Sie sie von der neuen Funktion auf, so dass Sie mit einer Funktion mit nur einem Schalter enden, und jeder Fall nur eine Funktion aufruft.

Sobald alle Funktionen verschoben sind, können Sie prüfen, ob Sie eine der anderen Funktionen umgestalten müssen. Beginnen Sie jedoch mit einem Komponententest, um sicherzustellen, dass die Funktionalität nicht verloren geht.

Jede der neuen Funktionen sollte auch eigene Komponententests haben, BTW.

0

Unsere CloneDR würde wahrscheinlich den geklonten Code genau identifizieren und Ihnen zeigen, wie man parametrisierten Code erstellt, um die einzelnen Klone zu ersetzen.

Sie können einen Clone-Bericht für C++ sehen und somit sehen, wie die vorgeschlagenen parametrisierten Clone-Blöcke am Link aussehen.