2009-07-20 12 views
8

Kiedy zaczynam pisać kod od zera, mam zły nawyk szybkiego pisania wszystkiego w jednej funkcji, cały czas myśląc "Sprawię, że stanie się bardziej modułowy później". Potem, gdy później nadejdzie, mam działający produkt i każda próba jego naprawy oznaczałaby tworzenie funkcji i wymyślanie tego, co muszę przekazać.Przezwyciężanie złego nawyku "późniejszego naprawienia"

Robi się najgorzej, ponieważ bardzo trudno jest przeprojektować klasy, gdy projekt jest już prawie gotowy. Na przykład zwykle planuję, zanim zacznę pisać kod, a potem, gdy mój projekt zostanie ukończony, zdałem sobie sprawę, że mogłem uczynić klasy bardziej modułami i/lub mogłem użyć dziedziczenia. Zasadniczo, nie sądzę, że robię wystarczająco dużo planowania i nie dostaję więcej niż jednego poziomu abstrakcji.

W końcu utknąłem w programie z dużą główną funkcją, jedną klasą i kilkoma funkcjami pomocniczymi. Nie trzeba dodawać, że nie nadaje się do wielokrotnego użytku.

Czy ktoś miał ten sam problem i miał jakieś wskazówki, aby przezwyciężyć ten problem? Jedną rzeczą, o której myślałem było napisanie głównej funkcji za pomocą pseduocode (bez wielu szczegółów, ale wystarczająco dużo, aby zobaczyć, jakie obiekty i funkcje potrzebują). Zasadniczo podejście odgórne.

Czy to dobry pomysł? Jakieś inne sugestie?

+1

Pytałem ostatnio o podobny temat dotyczący planowania projektu, ale nasze problemy są nieco inne. Może niektóre odpowiedzi mogą ci się przydać. http://stackoverflow.com/questions/1100819/how-do-you-design-object-oriented-projects – Victor

+0

Nagradzam się czekoladą. – Maxpm

Odpowiedz

5

"Najpierw tworzymy nasze zwyczaje, potem oni nas tworzą."

Wydaje się, że dotyczy to zarówno dobrych, jak i złych nawyków. Wygląda na to, że złapał cię zły.

Praktykuj bycie bardziej modułowym z góry, aż stanie się "po prostu tak, jak ja to robię".

3

Tak, rozwiązanie jest łatwe, chociaż przyzwyczajenie się do niego wymaga czasu. Nigdy nie twierdz, że będzie "później", gdzie usiądziesz i po prostu dokonasz refaktoryzacji. Zamiast tego kontynuuj dodawanie funkcjonalności do swojego kodu (lub testów) i podczas tej fazy wykonuj małe, przyrostowe refaktoryzacje. "Później" będzie w zasadzie "zawsze", ale ukryte w fazie, w której za każdym razem robisz coś nowego.

1

Uważam, że dyscyplina TDD Red-Green-Refactor działa cuda.

12

Podejrzewam zupełnie odwrotne podejście, pisząc coś w locie bez wcześniejszej fazy projektowania. Oznacza to, że piszę główną funkcję w taki sposób, w jaki wyglądałaby ona "w idealnym świecie", używając wyimaginowanych obiektów i metod, a następnie tworzyła szkielety wszystkiego, aby program się kompilował, a następnie wracał i działał. Zapewnia to modułową konstrukcję, a kod wysokiego poziomu jest bardzo łatwy do zrozumienia. Jeśli cokolwiek, wadą jest to, że kod może stać się zbyt modułowy, ponieważ kusi, by napisać doFoo() zamiast implementować foo inline.

+1

Tak, podążam tą samą drogą.Myślę, że pod koniec dnia nigdy nie można zapukać w modularyzowany kod, ale z mojego doświadczenia wynika, że ​​prawdziwą korzyścią jest łatwość zarządzania i dobra struktura. Aspekty ponownego wykorzystania są minimalne, ale pewne, że istnieją. –

+0

Dokładnie moje podejście. Jedynym możliwym niebezpieczeństwem jest to, że kod kończy się w następujący sposób: main() {doFoo(); } foo() {doFoo(); } doFoo() {reallyDoFoo(); } reallyDoFoo() {reallyReallyDoFoo(); } –

2

Moja zasada brzmi: wszystko powyżej 20 linii powinno być czyste. Każdy projekt IME opiera się na kilku "just-a-proof-of-concept", które nigdy nie miały zakończyć się kodem produkcyjnym. Ponieważ wydaje się to nieuniknione, nawet 20 linii kodu dowodu z koncepcji powinno być jasne, ponieważ mogą one być jednym z fundamentów dużego projektu.

Moje podejście jest z góry na dół. Piszę

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

i dopiero później zacznę pisać wszystkie te funkcje. (Zwykle są one inline i przechodzą do nienazwanej przestrzeni nazw, czasami okazują się one jednostronne, a następnie mogę je później wyeliminować.)

W ten sposób unikam również komentarza do algorytmów: nazwy funkcji są wystarczającym wyjaśnieniem.

0

Prawie zidentyfikowałeś problem. Brak wystarczającego planowania. Poświęć trochę czasu na przeanalizowanie rozwiązania, które zamierzasz rozwinąć, podziel je na części funkcji, określ, w jaki sposób najlepiej je wdrożyć i spróbuj oddzielić warstwy aplikacji (interfejs użytkownika, logika biznesowa, warstwa dostępu do danych, itp).

Myśl w kategoriach OOP i refaktora, o ile ma to sens. Jest o wiele tańsze niż robienie tego po zbudowaniu wszystkiego.

0

Pisz główną funkcję minimalnie, prawie bez niczego. W większości programów GUI, programach gier sdl, open gl lub cokolwiek z dowolnym interfejsem użytkownika, główną funkcją powinna być nic więcej niż pętla jedzenia eventowego. Musi to być lub zawsze będą długie okresy, w których komputer wydaje się nie odpowiadać, a system operacyjny myśli, że może go wyłączyć, ponieważ nie reaguje na wiadomości.

Po uzyskaniu głównej pętli, szybko ją zablokuj, ale tylko po to, aby modyfikować ją w celu usuwania błędów, a nie nowych funkcji. Może to po prostu spowodować przeniesienie problemu do innej funkcji, ale posiadanie funkcji monilitycznej jest mimo wszystko trudne do zrobienia w aplikacji opartej na zdarzeniach. Zawsze będziesz potrzebował milion małych programów obsługi zdarzeń.

Może masz klasę monolityczną. To zrobiłem. Głównie sposobem radzenia sobie z tym jest próba zachowania mentalnej lub fizycznej mapy zależności i zanotowanie, gdzie są ... powiedzmy perforacje, szczeliny, w których grupa funkcji nie jest jednoznacznie zależna od jakiegokolwiek współdzielonego stanu lub zmiennych z inne funkcje w klasie. Tam możesz wydzielić tę grupę funkcji w nową klasę. Jeśli to naprawdę wielka klasa i naprawdę zaplątana, nazwałabym to zapachem kodu. Pomyśl o przeprojektowaniu czegoś tak, by był mniej wielki i współzależny.

Inną rzeczą, którą możesz zrobić, jest kodowanie. Zwróć uwagę, że gdy funkcja powiększa się do rozmiaru, w którym nie pasuje już do jednego ekranu, prawdopodobnie jest za duża i na tym etapie zacznij myśleć o tym, jak złamać w dół na wiele mniejszych funkcji.

0

Refaktoryzacja jest o wiele mniej przerażająca, jeśli masz do tego dobre narzędzia. Widzę, że oznaczyłeś swoje pytanie jako "C++", ale to samo dotyczy dowolnego języka. Uzyskaj IDE, w którym wyodrębnianie i zmienianie nazw metod, wyodrębnianie zmiennych itp. Jest łatwe, a następnie naucz się, jak skutecznie używać tego IDE. Wtedy "małe, przyrostowe refaktoryzacje", o których wspominał Stefano Borini, będą mniej zniechęcające.

0

Twoje podejście niekoniecznie jest złe - wcześniej bardziej modułowa konstrukcja może okazać się zbyt zaawansowana.

Potrzebujesz refaktora - to jest fakt. Pytanie brzmi, kiedy? Za późno, a refaktoryzacja to zbyt duże zadanie i zbyt ryzykowne. Zbyt wcześnie i może to być nadmierna inżynieria. I, z biegiem czasu, będziesz musiał ponownie dokonać refaktoryzacji ... i znowu. To tylko część naturalnego cyklu życia oprogramowania.

Sztuką jest niedługo refaktor, ale nie za wcześnie. I często, ale nie za często. Jak szybko i jak często? Dlatego to sztuka, a nie nauka :)

Powiązane problemy