Niby proste, a jednak…
Na początku tego semestru, kiedy rozpoczęliśmy naszą uczelnianą przygodę z Javą, dostaliśmy na laboratoriach z tego przedmiotu zadanie. Mieliśmy przez cały semestr rozwijać aplikację do obsługi grafów. Ktoś mógłby powiedzieć „phi, banał, przecież można to w kilka godzin napisać”. Teoretycznie tak, i na początku sam tak sądziłem. Jednak przez te parę miesięcy moje myślenie o tym projekcie zmieniło się diametralnie.
Do tej pory wszystkie projekty związane ze studiami powstawały na zasadzie „byle by działało” i nie ma w tym nic odkrywczego, spora część studentów tak do tego podchodzi, w końcu po dostaniu zadowalającej oceny taki program w najlepszym razie ląduje w czeluściach dysku naszego komputera. Jednak laboratoria z języka Java pokazały, że program, którego zasada działania jest wręcz banalna, w końcu miał on potrafić jedynie wczytać, utworzyć, zapisać graf i dodawać, usuwać krawędzie i wierzchołki, może stać się wyzwaniem na kilka miesięcy.
Samo napisanie podstawowej funkcjonalności faktycznie zajęło te kilka godzin. Jednak podczas oceny tej pracy dowiedziałem się o ilu rzeczach kompletnie nie związanych z samą obsługą grafów zapomniałem lub nie byłem ich świadomy. W tym miejscu muszę powiedzieć, że prowadzący, dla którego nie liczy się jedynie to aby były w końcowej pracy zawarte wszystkie podpunkty z polecenia, jest szalenie motywujący, i pomimo krytyki ma się nadal chęć do poprawiania swojego „dzieła”, a luźniejsza atmosfera zachęca do wyjścia z domu i przyjścia na zajęcia.
No dobra, rozpisałem się, a wypadałoby teraz wspomnieć nieco szerzej o samym projekcie. Tak więc na pierwszych zajęciach dostaliśmy zadanie, alby napisać program, który będzie potrafił odczytać z pliku graf, dodawać i usuwać krawędzie/wierzchołki i ewentualnie zapisać go z powrotem do pliku. Jednak do tego wszystkiego dodane zostało, że „klienci często zmieniają swoje zdanie w trakcie prac” i jak się niedługo później okazało było kluczową wytyczną. Pierwsze wersje u większości osób były robione „po studencku”, tak jak to opisywałem kilka zdań wcześniej. Ale już po pierwszych komentarzach na ich temat musieliśmy wszyscy zmienić sposób myślenia (albo chociaż próbować to zrobić :P ). Nagle wymagania zaczęły rosnąć, chociaż funkcjonalność nie zmieniała się zbytnio. Efektem końcowym było to, że program:
- – powinien umożliwiać wczytywać, zapisywać grafy w dowolnej lokalizacji (niekoniecznie na lokalnym dysku) przy wsparciu dla URI i rozszerzanie go pod tym względem
- – dawać możliwość dodawania i usuwania krawędzi i wierzchołków
- – mieć oddzieloną warstwę prezentacji od warstwy logiki
- – umożliwiać podmianę elementów takich jak parser
- – odpowiednio reagować na błędy i nie kończyć nagle działania po wpisaniu niepoprawnych danych
- – logować wszystkie operacje do pliku z logami
- – zapewniać wsparcie dla pracy na wielu wątkach
- – powinien mieć kod napisany zgodznie z ogólnie przyjętymi zasadami
- – mieć dołączony diagram klas w UML
Jak widać jedynie dwa pierwsze założenia odnoszą się bezpośrednio do samych grafów. Jednak dopiero wszystkie te punkty składają się na kompletną aplikację, która działa nawet w trudnych sytuacjach i daje możliwość łatwej rozbudowy bez przeglądania całego kodu źródłowego. Nawet nie próbuję dowodzić, że mój kod zawiera te wszystkie wytyczne, bo tak nie jest. Nie jest on idealny, a nawet mogę śmiało powiedzieć, że do ideału to mu jeszcze bardzo dużo brakuje. Ale ponieważ zajęcia i projekt dobiegły końca więc chyba warto mimo wszystko pochwalić się tym co zrobiłem. Najpierw może zaprezentuję kod, który pokazuje jak wygląda użycie mojej biblioteki, bo właśnie biblioteką do obsługi grafów miał być ten projekt, na prostym przykładzie. Zadaniem tego fragmentu jest wczytanie grafu z pliku graf.txt, dodanie do niego nowego wierzchołka i zapisanie go do pliku graf2.txt. Moja biblioteka wymaga do tego tych kilku linijek:
import graphscollection.GraphsCollection; import graphscollection.SimpleParserBuilder; public class GraphSample { public static void main(String[] args) { GraphsCollection graphs = new GraphsCollection(SimpleParserBuilder.buildParser()); graphs.addPool("samplePool"); graphs.load(graphs.getGraphPool("samplePool"), "file://graf.txt"); graphs.executeCommand(graphs.getGraphPool("samplePool"), "addVertex#graf1:2:label1"); graphs.saveAll("file://graf2.txt"); } }
Jak widać, udało mi się stworzyć w miarę prosty w użyciu twór. Oczywiście można by użycie go uprościć, np. poprzez wprowadzenie domyślnego zbioru grafów, zamiast wymagania tworzenia go przed wczytaniem grafu z pliku, jednak przy większym projekcie korzystającym z biblioteki domyślny zbór i tak byłby zapewne zastąpiony innym, a dzięki temu, że nawet przy prostej aplikacji trzeba go utworzyć ręcznie, widać od razu jak w całości wygląda praca z tą biblioteką. Można by również zastąpić wywołanie komendy dodającej wierzchołek odpowiednią funkcją, ale moim zdaniem, w takiej postaci dodawanie kolejnym modyfikatorów jest prostsze, bo wymaga jedynie dodania odpowiedniej komendy do parsera.
Załączam również diagram klas, w końcu po coś został narysowany:
Architektura prosta, i tak jak cała reszta, nie idealna.
Ogólnie po tych 3 miesiąca pracy, wielu zmianach i wielokrotnym przepisywaniu wszystkiego od nowa mogę stwierdzić, że doszedłem do czegoś co nadaje się do pokazania, ale wymaga jeszcze wiele poprawek. Myślę jednak, że ważniejsze od samego dzieła jest to, czego się przez ten czas nauczyłem. Zrozumiałem, że przemyślenie i rozplanowanie projektu jest dużo ważniejsze od samego tworzenia go, bo dobry projekt pozwala zdecydowanie skrócić czas realizacji zadania, a zakładana funkcjonalność dostępna dla użytkownika jest tylko niewielką częścią całości.
Teraz nie pozostaje mi nic innego jak podzielenie się całością pracy oraz zabranie się za przepisanie projektu na jedyny słuszny język jakim jest C# dla platformy .NET :) Poniżej załącznik z kodem źródłowym biblioteki. Powstał w NetBeansie, jednak uznałem, że opublikuję jedynie źródła, ponieważ i tak w samych ustawieniach NetBeansa nie dokonywałem zmian więc wystarczy stworzyć nowy projekt i dodać do niego moje pliki.
Tutaj znajduje się archiwum z kodem -> Biblioteka do obsługi grafów
Leave a Comment