Jak pokonać paraliż dzięki analizie podczas kodowania?

37

Kiedy rozpoczynam nowy projekt, często od razu zaczynam myśleć o szczegółach wdrożenia. „Gdzie mam umieścić DataBaseHandler? Jak powinienem go używać? Czy klasy, które chcą go używać, powinny pochodzić z jakiejś superklasy abstrakcyjnej ..? metody wysyłania żądań i analizowania danych? ”

Skończyło się na zwlekaniu przez długi czas, ponieważ chcę kodować w celu rozszerzenia i ponownego użycia. Ale wydaje mi się, że prawie niemożliwe jest przeszłość, myśląc o tym, jak doskonale wdrożyć.

A potem, jeśli spróbuję powiedzieć „pieprzyć to, po prostu załatw sprawę!”, Uderzyłem szybko w ścianę z cegieł, ponieważ mój kod nie jest zorganizowany, mieszałem poziomy abstrakcji itp.

Jakie masz techniki / metody na rozpoczęcie nowego projektu przy jednoczesnej konfiguracji logicznej / modułowej struktury, która będzie dobrze skalowana?

- - EDYCJA - -

Cóż, jest to już pytanie, na które trudno jest zaakceptować odpowiedź, ale chciałem uzyskać więcej informacji zwrotnych, sprawdzić, czy istnieje jakiś konsensus. TDD brzmi naprawdę fajnie i, szczerze mówiąc, chciałem przyspieszyć korzystanie z JUnit itp. Jednocześnie, co fani TDD myślą o tym, że jeden słuszny punkt w odniesieniu do TDD rozwiązuje mój problem szczególnymi problemami jest to, że TDD tak naprawdę nie zajmuje się kwestią projektowania. Jasne, zgadzam się, że TDD pomoże mi zdefiniować, co chcę zrobić, a następnie będę mógł stopniowo pracować nad tym, jak to zrobić, ale istnieje wiele różnych ogólnych wzorów / struktur projektowych, które mogą przejść wszystkie testy jednostkowe. To wszystko: testuje pojedyncze JEDNOSTKI. Chyba jestem trochę zmieszany ... Nie wiem. Może ja'

Dzięki!

LuxuryMode
źródło
2
Cofnij się, weź długopis i papier, naszkicuj większy obraz. pomoże ci to zaprojektować implementację w bardziej ustrukturyzowany sposób, zamiast zatracić się w szczegółach ...
Darknight
To świetne pytanie. Jest to pułapka, w którą też popełniłem.
Corv1nus

Odpowiedzi:

16

Polecam używając Test-Driven-Development , trzeba się przyzwyczaić, szczególnie podczas pracy z dobrym IDE, takim jak zaćmienie, ale zalety są świetne.

Zasadniczo zapisujesz testy w kodzie, zanim napiszesz sam kod. Jesteś więc zmuszony spojrzeć na swój kod z punktu widzenia jego wykorzystania, co oznacza, że ​​twoje interfejsy ewoluują wraz ze wzrostem liczby wdrożonych scenariuszy.

Inną cechą jest to, że wdrażasz w bardzo małych porcjach (im większe, tym bardziej doświadczony jesteś w technice i programowaniu), więc zmusza cię do skupienia się na bardzo małym i dobrze zdefiniowanym problemie za każdym razem.

A także, ponieważ najpierw piszesz test, a dopiero potem wdrażasz, przed tobą czeka go test zakończony niepowodzeniem. Więc jeśli jesteś jak większość programistów, nie dasz się ponieść szalonej analizie, ponieważ pomyślisz: „Muszę sprawić, by ten test zadziałał”.

Krótki przykład w Javie:
Powiedz, że chcę opracować program, który czyta i zapisuje komunikat z bazy danych.

Więc zaczynam od pierwszej dobrze zdefiniowanej akcji, potrzebuję DB:

@Test
public void testDB() {
  DB db = DbConnector.getDB(address);
  assertNotNull(db);
}

ok, więc tutaj widzę, że muszę zaimplementować klasę DbConnector.getDB, aby zwróciła DB, do tego czasu ten test się nie powiedzie. Idę i robię to ...

Nie dodam następnej rzeczy, którą chcę zrobić, załaduj wiadomość z bazy danych:

@Test
public void testDB() {
  DB db = DbConnector.getDB(address);
  assertNotNull(db);
  String message = db.fetchMessage(key);
  assertEquals("hello world", message);
}

Teraz dodałem kolejną małą funkcję do DB, która polega na pobraniu wiadomości, idę i wdrażam ją, kiedy skończyłem, kontynuuję pracę nad jedną funkcją na raz, aż do osiągnięcia czegoś takiego:

@Test
public void testDB() {
  DB db = DbConnector.getDB(address);
  assertNotNull(db);
  String message = db.fetchMessage(key);
  assertEquals("hello world", message);
  message = "foo bar";
  db.storeMessage(message);
  message = db.fetchMessage();
  assertEquals("foo bar", message);
}

Może to wydawać się bardzo prostym przykładem, ale działa to również w przypadku bardziej złożonych zadań. Wiem, że na początku jest to bardzo czasochłonne, ale kiedy się do tego przyzwyczajasz, widzisz, że w rzeczywistości jest on znacznie wydajniejszy. Z jednej strony unikasz paraliżu poprzez analizę, a z drugiej otrzymujesz znacznie bardziej solidny kod, który zwykle ma mniej błędów i przechodzi mniej iteracji.

Asaf
źródło
4
A TDD zmusza cię do częstego refaktoryzacji, więc wchodzisz w tryb pracy ciągłego refaktoryzacji, który pomoże przebić się przez ceglaną ścianę pomieszanego kodu, jak wspomniano programmers.stackexchange.com/questions/86364/...
cringe
Uważam, że pierwszy aspekt testu TDD stanowi przeszkodę w tego rodzaju okolicznościach. Jeśli mam wystarczająco dużo problemów z zaprojektowaniem samego interfejsu, w jaki sposób zaprojektowanie testów będzie łatwiejsze lub bardziej oczywiste? To i obciążenie refaktora na samym początku próby zaprojektowania czegoś jest zdecydowanie za duże. Zastanawiam się, jak inni sobie z tym radzą.
Steven Evers,
1
@SnOrfus, prawda. TDD działa dobrze, gdy masz moduły i chcesz skoncentrować się na tym, co przeciwne. Ale te moduły mogą być zorganizowane na wiele sposobów. Jak są zgrupowane razem, jakiego rodzaju struktury użyjesz, tak naprawdę nie jest wyjaśnione za pomocą TDD.
LuxuryMode,
5
Hmmmm, to brzmi jak fanatyk TDD ... Co się stało z użyciem pióra i papieru do szkicowania architektury? a może jestem starą modą i nie jestem wystarczająco „hip”…
Darknight
1
Długopis i papier (lub tablica) są dobre. Naszkicuj ogólny plan, duży obraz. Jeśli nie mieści się na kartce papieru, jest zbyt skomplikowane. Po opracowaniu dużego planu zdjęciowego możesz zająć się BDD, kpiną itp.
Donal Fellows,
10

To mi się przydarza, więc nabrałem zwyczaju przyjmowania (i przyjmowania) sposobu myślenia o ciągłym refaktoryzacji. Robię najprostszą rzecz, która mogłaby zadziałać, potem sprzątam, organizuję, rozłączam, testuję i idę dalej.

Nie oznacza to, że nie planuje się dużo, ale dzieje się to bardzo szybko i częściej, jak gryzmoły na złom lub w mojej głowie. Podsumowując, czasami nazywam to małym procesem mikro-iteracji, ponieważ każda zajmuje 5-20 minut, a z doświadczenia potrzeba 2-3, aby dokończyć to, nad czym pracuję (oczywiście w zależności od tego, co robię).

Na marginesie: Nauczałem wiele osób w różnych formach pisania (raporty, eseje i ogólnie pismo techniczne) i w ten sam sposób zachęcam ich do pisania rzeczy w celu przezwyciężenia bloku pisarza. „Po prostu wypuść na stronę wszystko, co przychodzi ci na myśl. Następnie nadamy temu sens, podzielimy to na akapity i sprawdzimy przebieg. W razie potrzeby przepiszemy to jeszcze raz”.

Steven Evers
źródło
1
+1 za wzmiankę o pisaniu. Niedawno przyjąłem metodę częstego refaktoryzacji kodowania i zastosowałem ją do pisania; działa dla mnie bardzo dobrze.
Zsolt Török
2

Kilka rzeczy, które mogą działać:

  • Zidentyfikuj podstawowy problem, który próbujesz rozwiązać - jaki jest sedno rzeczy, którą chcesz zrobić? Zaimplementuj tylko to i absolutnie minimum kodu pomocniczego, aby go uruchomić. Gdy wszystko zadziała zgodnie z twoją satysfakcją, twórz iteracyjnie, refaktoryzując bez litości na każdym kroku.
  • Sprawdź, czy działają inne paradygmaty programowania. Pomimo wszystkich swoich zalet programowanie obiektowe nie jest odpowiedzią na wszystkie problemy i nie wszystkie mózgi programistów działają w ten sposób. Wybierz (czysty) język funkcjonalny; napisz kod proceduralny; zanurkuj do poziomu sprzętowego i zrób trochę C, a może nawet asemblera; itp. Kilka języków, które mogą wstrząsnąć twoim umysłem (zakładając, że obecnie używasz czegoś takiego jak C ++ / Java / C # / VB / ...): Haskell, ML, Lisp (różne dialekty do wyboru), Erlang, Prolog, Smalltalk, JavaScript (jeśli przestaniesz próbować sprawić, by zachowywał się jak Java i zamiast tego przyjmiesz jego charakter zamknięcia), C, Pascal, awk i prawdopodobnie kilkanaście innych. Kluczową cechą jest to, że muszą się bardzo różnić od tego, z czego korzystasz teraz. To nie jest coś, co chcesz zrobić przy dużym projekcie o dużym zagrożeniu,
  • Użyj radykalnie innej metody projektowania. Sprawdź, czy możesz podnieść projekt pod innym kątem. Zakładam, że zwykle zaczynasz projektować od ułożenia zajęć; a może zaczniesz od struktur danych dla odmiany? A może najpierw zaprojektujesz interfejs użytkownika, dosłownie rysując formularze wejściowe przed zaprojektowaniem jakiejkolwiek funkcjonalności?
tdammers
źródło
1

W przypadku wielu decyzji projektowych może pomóc wykonać „skok”, który jest krótkim, ograniczonym czasowo wysiłkiem badawczym, w którym można zbadać niektóre opcje architektury lub projektu, kodując do prototypu typu „wyrzucany”. Na przykład możesz zapoznać się z wykorzystaniem biblioteki typu open source lub sposobem organizacji zajęć i interfejsów. Najważniejsze jest, aby krótko mówiąc, abyś mógł wypróbować inne podejście, jeśli pierwsze jest niezadowalające, i mam nadzieję, że zdobędziesz wystarczającą wiedzę w ćwiczeniu, aby lepiej podejmować decyzje architektoniczne lub udowodnić koncepcję. Samo ćwiczenie wymaga natychmiastowego kodowania, co pomaga wydostać się z „bloku pisarzy” bez konieczności zbyt wczesnego angażowania się w „git 'er done”.

Następnie korzystne jest zastosowanie podejścia TDD lub BDD, o którym wspomniał Asaf, aby kontynuować wdrażanie projektu.

Dozorca więzienny
źródło
+1 Zgadzam się. Planuj odrzucić pierwszą próbę. Traktuj to jako doświadczenie edukacyjne. Wyrzuciłem aż sześć, zanim myślę, że chcę zostać przy siódmym.
Mike Dunlavey
1

Nie będziesz go potrzebować , więc nie myśl za dużo na początku.

Zainwestuj więcej czasu na zdefiniowanie, zrozumienie celu i problemu.

„Rozszerzalność i możliwość ponownego użycia” jest naturalnym wynikiem cyklu życia dobrze napisanych programów.

9dan
źródło
0

Zakładam, że patrzymy na projekt średniej wielkości.
Zacznę od wejścia na deskę kreślarską. Zanim to zrobisz, musisz przygotować swoje wymagania funkcjonalne i niefunkcjonalne. Najpierw wymyślisz architekturę oprogramowania, tj. Spojrzysz na dowolne wzory architektoniczne, które będą pasować do twoich wymagań.
Kiedy już zdecydujesz, jak twoja architektura będzie wyglądać, powinieneś przejść do projektu niskiego poziomu, tj. Spojrzeć na wszystkie byty, klasy i funkcjonalność . Tutaj ponownie spróbujesz zidentyfikować wzorce projektowe, które pasują. W tym procesie będziesz wiedzieć, jakie są twoje podstawowe klasy i potrzebne interfejsy.
Następnie możesz zbudować strukturę i uruchomić szybkie testy, aby sprawdzić, czy to spełnia wszystkie niefunkcjonalne wymagania
Następnie wybrałbym Test Driven Development, jak sugeruje @Asaf.

Pamiętaj, że pomimo spędzania czasu na projektowaniu i architekturze, zawsze bądź gotów odwiedzić architekturę, jeśli zajdzie taka potrzeba.

hangar18
źródło
0

Myślę, że to świetne pytanie i nic nie będzie działać dla wszystkich. Myślę, że taki paraliż jest naturalnym produktem ubocznym stania się coraz bardziej kompetentnym w swojej dziedzinie. To powiedziawszy, oto kilka rzeczy, które robię, które pomagają, ale nie rozwiązują problemu:

  • Odłóż na bok swój nieskazitelny projekt i pracuj nad wersją Fugly. To jest wersja, w której mówisz sobie: Kod nie powinien być ładny. W rzeczywistości, powiedz sobie, poważna refaktoryzacja i ponowne formatowanie jest niedozwolone. Niech będzie całkowicie niezorganizowany i uwolnij się od więzów dobrego kodowania. b. To po prostu musi działać. do. Zawsze zaskakuje mnie to, czego dowiaduję się o przestrzeni problemowej, kiedy wyrzucam wszystkie inne obawy. Skończyłem też z drobnymi ciekawostkami, które często pomagają mi znaleźć właściwy projekt w bardziej oświecony sposób.

  • Odłóż spory blok czasu na projekt, bez komputera. Spróbuj pojąć, co naprawdę próbujesz osiągnąć, i poszukaj magicznego zen, który wykracza poza szaleństwo OO / Design Pattern.

Kevin Hsu
źródło
0

Daj konkretny wyraz swoim myślom: zapisz / wpisz je, wyciągnij lub cokolwiek innego. Pomoże ci to w ponownym przemyśleniu myśli w razie potrzeby; powstrzyma cię przed chodzeniem w kółko; pomaga myśleć jaśniej.

Ilekroć widzę, że nigdzie i wszędzie myślę o czymś, wpisuję je i pomaga mi to jasno myśleć.

Srisa
źródło
0

Zwykle zaczynam od zera, tworzę najprostszy możliwy prototyp i uruchamiam coś. Skorzystaj z prototypu, aby dokonać inżynierii wstecznej przypadków testowych szczęśliwej ścieżki, przypadków testowych do sterowania interfejsami, a następnie pomyśl o umowach przed / po, aby pomóc w zbudowaniu zasięgu testu.

Nie przejmuj się abstrakcją, optymalizacją lub weryfikacją, dopóki problem nie zostanie w pełni zrozumiany.

sbrenton
źródło