2009-07-20 5 views
8

Wenn ich anfange Code von Grund auf neu zu schreiben, habe ich eine schlechte Angewohnheit, schnell alles in einer Funktion zu schreiben, die ganze Zeit denkend "Ich mache es später modularer". Dann, wenn später kommt, habe ich ein funktionierendes Produkt und alle Versuche, es zu reparieren, würde bedeuten, Funktionen zu schaffen und herauszufinden, was ich passieren muss.Die schlechte Angewohnheit des "Reparierens später"

Es wird am schlimmsten, weil es extrem schwierig wird, Klassen neu zu gestalten, wenn Ihr Projekt fast fertig ist. Zum Beispiel plane ich normalerweise etwas, bevor ich mit dem Schreiben von Code anfange, und als mein Projekt fertig ist, merke ich, dass ich die Klassen modularer gestalten und/oder die Vererbung verwenden könnte. Im Grunde glaube ich nicht, dass ich genug Planung mache und ich bekomme nicht mehr als eine Abstraktionsebene.

Also am Ende bin ich mit einem Programm mit einer großen Hauptfunktion, einer Klasse und ein paar Hilfsfunktionen fest. Es ist unnötig zu sagen, dass es nicht sehr wiederverwendbar ist.

Hat jemand das gleiche Problem und irgendwelche Tipps, um dies zu überwinden? Eine Sache, die ich im Sinn hatte, war, die Hauptfunktion mit pseduocode zu schreiben (ohne viel Detail, aber genug um zu sehen, welche Objekte und Funktionen sie brauchen). Im Wesentlichen ein Top-Down-Ansatz.

Ist das eine gute Idee? Irgendwelche anderen Vorschläge?

+1

Ich habe kürzlich eine ähnliche Frage bezüglich der Projektplanung gestellt, aber unsere Probleme sind etwas anders. Vielleicht könnten dir einige Antworten von Nutzen sein. http://stackoverflow.com/questions/1100819/how-do-you-design-objektorientierte-projekte – Victor

+0

Ich belohne mich mit Schokolade. – Maxpm

Antwort

5

"Zuerst machen wir unsere Gewohnheiten, dann machen sie uns."

Dies scheint für gute und schlechte Gewohnheiten zu gelten. Klingt wie ein schlechter hat dich ergriffen.

Üben Sie modularer im Voraus, bis es "genau so ist, wie ich Dinge mache."

3

Ja, die Lösung ist einfach, obwohl es einige Zeit braucht, um sich daran zu gewöhnen. Niemals behaupten, dass es ein "späteres" geben wird, wo Sie sich hinsetzen und einfach Refactoring machen. Stattdessen fügen Sie dem Code (oder den Tests) weitere Funktionen hinzu und führen während dieser Phase kleine, inkrementelle Refactorings durch. Das "spätere" wird grundsätzlich "immer" sein, aber versteckt in der Phase, in der man jedes Mal etwas Neues macht.

1

Ich finde die TDD Red-Green-Refactor-Disziplin wirkt Wunder.

12

Ich nehme den völlig entgegengesetzten Ansatz, wenn ich etwas ohne eine große Design-Phase im Vorhinein schreibe. Das heißt, ich schreibe die Hauptfunktion so, wie es "in einer perfekten Welt" aussehen würde, indem ich imaginäre Objekte und Methoden benutze, dann Skelette von allem erzeuge, so dass das Programm kompiliert, dann zurückgehe und es zum Laufen bringe. Dies gewährleistet einen modularen Aufbau und der Code auf hoher Ebene ist sehr einfach zu verstehen. Wenn überhaupt, ist der Nachteil, dass der Code zu modular werden kann, da es verlockend ist, doFoo() zu schreiben, anstatt foo inline zu implementieren.

+1

Ja, ich folge diesem Ansatz.Ich denke, am Ende des Tages können Sie niemals Modularisierten Code knacken, aber meiner Erfahrung nach ist der wirkliche Vorteil die Verwaltbarkeit und einfach eine gute Allround-Struktur. Die Wiederverwendungsaspekte sind minimal, aber sicher, dass sie existieren. –

+0

Genau meine Vorgehensweise. Die einzige mögliche Gefahr ist, dass der Code so aussieht: main() {doFoo(); } foo() {doFoo(); } doFoo() {wirklichDoFoo(); } reallyDoFoo() {wirklichReallyDoFoo(); } –

2

Meine Faustregel ist, dass alles, was länger als 20 LoC ist, sauber sein sollte. IME Jedes Projekt basiert auf ein paar "Just-a-Proof-of-Concept" -Konzepten, die nie im Produktionscode enden sollten. Da dies jedoch unvermeidlich ist, sollten sogar 20 Zeilen des Proof-of-Concept-Codes klar sein, weil sie letztendlich eine der Grundlagen eines großen Projekts sein könnten.

Mein Ansatz ist von oben nach unten. Ich schreibe

while(obj = get_next_obj(data)) { 
    wibble(obj); 
    fumble(obj); 
    process(filter(obj)); 
} 

und nur beginnen, alle diese Funktionen später zu schreiben. (Normalerweise sind sie inline und gehen in den unbenannten Namespace. Manchmal erweisen sie sich als Einzeiler und dann kann ich sie später eliminieren.)

So vermeide ich auch, die Algorithmen kommentieren zu müssen: Die Funktionsnamen sind Erklärung genug.

0

Sie haben das Problem ziemlich genau identifiziert. Nicht genug Planung. Analysieren Sie die Lösung, die Sie entwickeln werden, analysieren Sie sie, zerlegen Sie sie in Funktionen, identifizieren Sie, wie sie am besten implementiert werden, und versuchen Sie, die Schichten der Anwendung zu trennen (Benutzeroberfläche, Geschäftslogik, Datenzugriffsebene). etc).

Denken Sie in Bezug auf OOP und Refactor so früh wie es Sinn macht. Es ist viel billiger als es zu tun, nachdem alles gebaut ist.

0

Schreiben Sie die Hauptfunktion minimal, mit fast nichts darin. In den meisten GUI-Programmen, SDL-Spieleprogrammen, Open-GL oder irgendetwas mit irgendeiner Art von Benutzerschnittstelle sollte die Hauptfunktion nichts anderes als eine Event-Eating-Schleife sein. Es muss sein, oder es wird immer lange Zeitabschnitte geben, in denen der Computer nicht reagiert, und das Betriebssystem denkt darüber nach, es möglicherweise herunterzufahren, weil es nicht auf Nachrichten reagiert.

Sobald Sie Ihre Hauptschleife, schnell sperren, nur für Fehlerbehebungen, nicht neue Funktionalität geändert werden. Dies kann am Ende das Problem auf eine andere Funktion verschieben, aber eine monilithische Funktion ist in einer ereignisbasierten Anwendung ohnehin ziemlich schwierig. Du wirst immer eine Million kleiner Event-Handler brauchen.

Vielleicht haben Sie eine monolithische Klasse. Ich habe das getan. Hauptsächlich geht es darum, eine mentale oder physische Karte von Abhängigkeiten zu erstellen und zu notieren, wo es ... Perforationen oder Spalten gibt, bei denen eine Gruppe von Funktionen nicht explizit von einem gemeinsamen Zustand oder Variablen abhängt andere Funktionen in der Klasse. Dort können Sie diesen Cluster von Funktionen in eine neue Klasse umwandeln. Wenn es wirklich eine riesige Klasse ist und wirklich verwickelt ist, würde ich das als Code-Geruch bezeichnen. Denken Sie darüber nach, ein solches Ding so neu zu gestalten, dass es weniger groß und interdependent ist.

Eine andere Sache, die Sie tun können, ist wie Sie programmieren, beachten Sie, dass, wenn eine Funktion zu einer Größe wächst, wo es nicht mehr auf einem einzigen Bildschirm passt, ist es wahrscheinlich zu groß, und an diesem Punkt beginnen darüber nachzudenken, wie zu brechen es in mehrere kleinere Funktionen herunter.

0

Refactoring ist viel weniger gruselig, wenn Sie gute Werkzeuge dafür haben. Ich sehe, dass du deine Frage mit "C++" markiert hast, aber das gleiche gilt für jede Sprache. Holen Sie sich eine IDE, in der das Extrahieren und Umbenennen von Methoden, Extrahieren von Variablen usw. einfach ist, und lernen Sie dann, wie Sie diese IDE effektiv nutzen. Dann werden die "kleinen, inkrementellen Refactorings", die Stefano Borini erwähnt, weniger beängstigend sein.

0

Ihr Ansatz ist nicht unbedingt schlecht - früher könnte mehr modulares Design als Over-Engineering enden.

Sie müssen umgestalten - das ist eine Tatsache des Lebens. Die Frage ist wann? Zu spät, und das Refactoring ist zu groß und zu risikoanfällig. Zu früh, und es könnte übertrieben sein. Und im Laufe der Zeit müssen Sie wieder umgestalten .. und noch einmal. Dies ist nur ein Teil des natürlichen Lebenszyklus von Software.

Der Trick ist Refactor bald, aber nicht zu früh. Und häufig, aber nicht zu häufig. Wie bald und wie oft? Deshalb ist es eine Kunst und keine Wissenschaft :)

Verwandte Themen