Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Gubienie się w dużym projekcie

Ostatnio zmodyfikowano 2018-05-01 22:19
Autor Wiadomość
RazzorFlame
Temat założony przez niniejszego użytkownika
Gubienie się w dużym projekcie
» 2018-04-30 21:34:56
Projekt kilkanaście tysięcy linijek kodu, ogarniacie jakiś większy temat i w pewnym momencie zastanawiacie się
"co w ogóle ja miałem zrobić?"
Przez to marnuje tak ogromną ilość czasu, że aż mi jest wstyd.
Też tak macie? Jeśli tak, to jak sobie radzicie z tym? Jest jakaś inna działająca metoda, niż "zmuś się i siedź aż coś wymyślisz"?

Zaczynam się przez to bardzo denerwować, bo mam ustalony swój cel ale nie idę do przodu bo ciągle się gubię.
inb4: kod staram się pisać przejrzyście, także raczej to problem koncepcyjny - jest tak wiele założeń, o których muszę pamiętać, że w końcu nic z tego nie wychodzi.

Próbowałem sobie rozrysowywać problemy: na kartce marnie wychodzi, bo ciężko się poprawia, programy do diagramów zwykle oferują UMLa, którego nie potrafię przyjąć - wszystko zwykle wygląda tak samo, ponadto nie chce projektować szczegółowo klas tylko raczej większe ogólniki.

Plx help bo już nie wiem co mam robić.
P-170880
DejaVu
» 2018-04-30 22:06:28
Quiz:
1. Czy masz klasy, które mają dedykowaną odpowiedzialność? Tj. ma łatwy w użyciu interfejs, łatwo napisać do niej testy jednostkowe i nie zależą one od innych klas (a nawet jeżeli są już zależności to też używasz klas zależnych jako pudełko)?
2. Czy dokumentujesz swój kod (co robi każda metoda)?
3. Czy piszesz w ogóle testy jednostkowe do klas, które mają nietrywialną logikę?
4. Czy jesteś w stanie powiedzieć jaka jest odpowiedzialność każdej klasy?
5. Czy używasz dużo dziedziczenia?
6. Czy piszesz dużo własnych szablonów?
7. Czy tworzysz własne biblioteki? Jeżeli tak to ile masz bibliotek i ile masz średnio klas na bibliotekę?

/edit:
Jak już odpowiesz na powyższe pytania to będzie można cokolwiek więcej powiedzieć. Możesz też przeczytać artykuł Dobre praktyki wytwarzania oprogramowania i dać informację zwrotną tj. czego Ci w nim zabrakło w kontekście Twojego problemu.
P-170881
RazzorFlame
Temat założony przez niniejszego użytkownika
» 2018-04-30 22:19:56
1. Jest wiele takich klas, są dość zróżnicowane. Ot, choćby klasy Vector2, Vector3, Polygon2 itd.
2. Tak, używam komentarzy XML
3. Od niedawna zaczynam, ale na większą skalę (w tym większym projekcie) tego nie robiłem
4. Tak, do czasu kiedy nie zabiorę się za coś większego. Problem jest w tym, że kiedy daję sobie jakieś większe zadanie, to nawet jeśli sobie rozpiszę co tam ma się dziać, to w pewnym momencie czuję, że nie jestem w stanie tego objąć jakby "jednym rzutem", że mój mózg nie jest w stanie w jednej chwili zobrazować sobie tego całego. Ostatnio próbowałem wizualizować sobie to wszystko na jakimś ładnym schemacie (takim jak DGML w Visual Studio) i czuję, że to może być to, ale szybko się zniechęciłem. Powodem było to, że VS przy grupowaniu wywala całe ułożenie node-ów, które sobie zrobiłem i ustawia według jakiegoś durnego swojego layoutu.
5. Nie wiem, czy rozumiemy to samo poprzez "dużo". To co miałem napisane do czasu, kiedy tego większego zadania nie zacząłem, posiadało ładną hierarchię, którą bez problemu byłbym w stanie opisać. Trochę dziedziczenia tam było, ale nawet teraz to dobrze rozumiem.
6. Tak, ale to się tyczy tylko biblioteki do matematyki. W innych sytuacjach raczej nie używam aż tak często szablonów.
7. Nie mam wielu bibliotek. Ten projekt, który tworzę jest biblioteką. Akurat dziś z rana postanowiłem wyodrębnić pliki odpowiadające stricte za matematykę, które kopiowałem z projektu do projektu i zrobić to jako osobną bibliotekę.
Oto ta biblioteka:
https://github.com/PoetaKodu​/quickmaffs/

A to link do projektu, który tworzę:
https://github.com/PoetaKodu​/advancedgdk/
Jest teraz w takim stadium, że nawet się nie kompiluje. Moim następnym celem będzie właśnie sprawienie, że ten większy projekt będzie korzystał z tej biblioteczki do matematyki, a nie z własnej kopii plików.
P-170882
DejaVu
» 2018-04-30 22:32:26
Proponowałbym Ci zmniejszyć liczbę podkatalogów. Biblioteka może w praktyce posiadać tylko dwa katalogi:
1. src/include => *.hpp
2. src => *.cpp, *.vcxproj

Krążenie po zagnieżdżeniach jest niewygodne. Jeżeli koniecznie chcesz podzielić coś na 'logiczne' części to służą do tego wirtualne katalogi (filtry), które można łatwo utworzyć w projekcie i przeciągać pliki do odpowiednich lokalizacji.

/edit:
Skoro projekt Ci się nie kompiluje to znaczy, że wszedłeś w fazę jego refaktoryzacji. Te są bardzo męczące, bo w praktyce nie wytwarzasz projektu tylko przenosisz kod i go poprawiasz. Im więcej masz kodu tym dłużej będzie Ci zajmowała refaktoryzacja. Cel jest słuszny jeżeli tworzysz biblioteki, jednak musisz pogodzić się z tym, że przez kilka tygodni/miesięcy prace nad projektem się zatrzymają na rzecz refaktoringu. Po refaktoringu zapomnisz sporo o właściwym projekcie i wejdziesz w uciążliwy proces 'wdrażania się' w projekt. Tego nie przeskoczysz niestety...
P-170883
RazzorFlame
Temat założony przez niniejszego użytkownika
» 2018-04-30 22:51:56
Te katalogi IMO są bardzo wygodne. To działa tak, że na repozytorium mam czasem więcej niż 1 projekt, więc grupowane są w katalogi. Następnie w tym katalogi znajduje się folder include i src i w każdym z nich jest folder o nazwie takiej jak projekt. Takie rozwiązanie pozwala na includowanie plików w ten sposób:
C/C++
#include <ProjectName/HeaderName.hpp>
Zaczerpnąłem to z SFMLa:
https://github.com/SFML/SFML
tylko, że oni przenieśli include i src folder wyżej. W każdym razie akurat foldery to jest to, z czym jest mi bardzo wygodnie.
Czytam właśnie teraz ten artykuł, pod koniec napiszę, co się dowiedziałem i czy coś z niego mi pomogło.

Skoro projekt Ci się nie kompiluje to znaczy, że wszedłeś w fazę jego refaktoryzacji.
Nie wiem, czy tak to można nazwać, ale mogę się mylić. Prawda jest taka, że pisałem to zadanie i miałem zastój. Potrzebowałem pokazać projekt koledze, bo szukałem pomocy i wrzuciłem niekompilujący się kod. To nieprofesjonalne, wiem.
=====
Edit:
Teraz zdałem sobie sprawę, że źle korzystałem z gita... powinienem kolejne moduły na nowych branchach pisać. Brawo ja... to pewnie też by wiele pomogło.
=====

Największy problem na moje oko mam z planowaniem. Ten projekt robię sam i sam muszę sobie wyznaczać cele.
Teraz moim zadaniem jest zrobienie "streamera". W skrócie: moja biblioteka dotyczy SAMPa, który ma sztywno narzucony limit obiektów (elementów mapy). Ten limit jest dość niski i zadaniem streamera jest sprawnie usuwać i tworzyć te obiekty, tylko gdy ktoś koło nich się znajduje.
Przerosło mnie to. Jak sobie tak analizowałem, to takie rzeczy muszę przemyśleć, napisać, a potem połączyć:

- Dzielenie świata gry na chunki (sześciany)
- Rejestrowanie i wyrejestrowywanie obiektów i graczy gdy znajdą się na mapie
- Śledzenie pozycji obiektów i graczy
- Algorytm, który sprawdza czy dany obiekt powinien być zespawnowany (to chyba najtrudniejsze)

A przede wszystkim, trzeba to napisać w jakiś zgrabny sposób i połączyć to z istniejącym kodem. Nie podaje jakichś większych szczegółów, bo to nie ma teraz sensu. W efekcie, jest tyle rzeczy do zrobienia, że nie jestem w stanie tego na raz sobie w myślach zobrazować. Nie wiem często od czego zacząć.
P-170884
jankowalski25
» 2018-05-01 00:02:07
3. Czy piszesz w ogóle testy jednostkowe do klas, które mają nietrywialną logikę?
3. Od niedawna zaczynam, ale na większą skalę (w tym większym projekcie) tego nie robiłem
Po analizie tego kawałka losujka mi podpowiada "najpierw napisz testy", więc chyba możesz tego spróbować. Wyobraź sobie, że to wszystko już jest jakoś zaimplementowane. Napisz prosty test. Uruchom go. Oczywiście powinien się wywalić, więc dopisz nieco kodu. Rozwijaj równolegle testy i kod, pamiętając o tym, aby najpierw pisać testy, a później implementować konkretną funkcjonalność.
P-170885
DejaVu
» 2018-05-01 11:56:33
Wydaje mi się, że trochę za dużo typów produkujesz.

https://github.com/PoetaKodu/quickmaffs/blob/master/quickmaffs/include/QuickMaffs/Math/Shapes/Ball.hpp

Posiadając tyle typów i chcąc wesprzeć je wszystkie wymuszasz robienie kolejnych szablonów. Przykładowo: chcesz coś w grze zrobić i wykorzystać do tego klasę Circle. Której użyjesz? Zrobisz kod, który będzie mógł użyć dowolnego typu? Czy zrobisz kod, który będzie używał i tak floata/double-a?

Biblioteka boost ma praktycznie wszystko zrobione w oparciu o szablony. Czy chcesz z niej korzystać? Wydaje mi się, że niekoniecznie.

Przykładowe API:
C/C++
class Path final
{
public:
    // Metoda zwraca ścieżkę katalogu ze ścieżki do pliku.
    static std::string getDirFromPath( const std::string & _path );
    static std::wstring getDirFromPath( const std::wstring & _path );
   
    // Metoda zwraca nazwę pliku ze ścieżki.
    static std::string getNameFromPath( const std::string & _path );
    static std::wstring getNameFromPath( const std::wstring & _path );
   
    // Metoda zwraca nazwę pliku (bez rozszerzenia) ze ścieżki.
    static std::string getNameFromPathNoExt( const std::string & _path );
    static std::wstring getNameFromPathNoExt( const std::wstring & _path );
   
    // Metoda zwraca rozszerzenie pliku ze ścieżki.
    static std::string getExtFromPath( const std::string & _path );
    static std::wstring getExtFromPath( const std::wstring & _path );
   
    // Metoda zmienia wystąpienia '\\' na '/'.
    static std::string changeToSlashes( const std::string & _path );
    static std::wstring changeToSlashes( const std::wstring & _path );
   
    // Metoda zmienia wystąpienia '/' na '\\'.
    static std::string changeToBackslashes( const std::string & _path );
    static std::wstring changeToBackslashes( const std::wstring & _path );
   
    // Metoda zmienia wystąpienia '/' lub '\\' na wystąpienia typowe dla używanego systemu operacyjnego.
    static std::string changeToPlatformSlashes( const std::string & _path );
    static std::wstring changeToPlatformSlashes( const std::wstring & _path );
   
    // Metoda upraszcza ścieżki. Usuwa nadmiarowe ukośniki, kropki, zamienia ukośniki
    // na preferowane na danej platformie.
    // Przykładowo:
    // "Asd//123\\.\\../a.xyz" zmienia na "Asd/a.xyz"
    static std::string simplifyPath( const std::string & _path );
    static std::wstring simplifyPath( const std::wstring & _path );
   
    // Metoda modyfikuje podaną ścieżkę usuwając z niej prefiks (ścieżka wynikowa jest
    // względna wzgledem prefiksu). Zwraca true, jeśli ścieżka miała dany prefiks, w
    // przeciwnym wypadku false.
    // Oba argumenty są upraszczane, więc "A/B/.." nie jest prefiksem "A/B/../../C".
    static bool removePrefix( std::string & _path, const std::string & _prefix );
    static bool removePrefix( std::wstring & _path, const std::wstring & _prefix );
   
private:
    Path();
}; // class Path
To samo API mógłbyś zrobić w oparciu o szablony i wcisnąć dodatkowo implementację metod w kod. Czy uważasz, że poziom czytelności byłby zachowany, gdyby tu się pojawiły szablony? Czy faktycznie masz potrzebę korzystania z innych typów tekstowych jak piszesz aplikację? W praktyce pisanie wszystkiego jako wstring załatwia Ci wszelkie problemy kodowania, każdy tekst przechowasz i nie musisz potem wykonywać żadnych konwersji. Z kolei string to zaszłość historyczna - czasem się przydaje przez to, że większość bibliotek jest pisanych na const char*.

Z mojego punktu widzenia szablony nie są czymś co należy nadużywać w bibliotekach, bo po prostu poziom skomplikowania kodu niepotrzebnie rośnie. Kontenery danych -> super sprawa dla szablonów. Typy danych i inne pierdoły -> to już jest przesada.

/edit:
W każdym razie fajnie, że masz kod ustandaryzowany. Kod całkiem ładnie wygląda, także good job za posiadanie standardów :)

/edit:
Jedyne co mi przychodzi do głowy to podzielenie pracy na etapy. Zrób jedną funkcjonalność od początku do końca, pokryj testami jednostkowymi, aby mieć gwarancję, że działa i potem pójdź do kolejnej funkcjonalności. Być może podejście funkcjonalności ułatwi Ci pracę z własnym kodem, bo widziałem, że w różnych klasach masz TODO i idziesz do dalszego kodu, w którym również zostawiasz po jakimś czasie TODO.

/edit:
Dodam jeszcze, że trudno będzie Ci pracować z testami jednostkowymi, jeżeli nie zapewnisz sobie środowiska, które automatycznie będzie je uruchamiało po każdym commicie.
P-170888
jankowalski25
» 2018-05-01 13:51:08
Jeszcze dodam, że jeśli pierwszy raz próbujesz pisać testy, to możesz zacząć od prostych asercji. Wtedy po prostu wstawiasz warunek, który ma być prawdziwy i idziesz dalej. W przeciwieństwie do niektórych języków, w C++ asercje są implementowane dość rozsądnie i na przykład nie rzucają wyjątków, tylko wywalają się podając nazwę pliku i numer linii (plus ewentualnie nazwę funkcji, jeśli tego potrzebujesz). Narzędzia z
#include <cassert>
 starczą na początek, a jak już trochę tego naklepiesz, to wtedy możesz pomyśleć nad jakimś bardziej rozbudowanym systemem testów lub rozszerzyć nieco klasyczną asercję o dodatkowe możliwości, jeśli w ogóle zajdzie taka potrzeba.
P-170889
« 1 » 2
  Strona 1 z 2 Następna strona