Stos a podział na klasy w prostym FileExplorerze JavaFX

0

Witajcie!

Ostatnio zajmowałem się NIO i chciałem zakończyć naukę nad rozdziałem tworząc mały eksplorator plików. Wykorzystuję przy tym Javę FX (8). Trafiłem na problem, którego sam nie mogę rozgryźć. Chcę wykorzystać obiekt typu Stack, aby zapisywać poszczególne obiekty typu Path katalogów, w których user się znajdował. Korzystać z nich następnie będzie Button służący do cofnięcia się do poprzednich lokalizacji. Chwilowo nie zakładam żadnych ograniczeń, co do ilości wpisów.
Pytanie brzmi - gdzie najwygodniej jest ten stack umieścić?
Bo korzystają z niego dwie klasy Buttons oraz GUI - w tej pierwszej dwa przyciski (może być ich więcej niedługo) a w tej drugiej TableView.

Cały kod jest na Bitbuckecie.

Jakieś uwagi na przyszłość? (Zauważyłem, że sensowny podział na klasy i metody to czynność, w której mam najmniejszy progress)

1

Fajnie że użyłeś JavaFX, zamiast przestarzałego Swinga, a tak zwykle robi dużo osób tutaj. Niefajnie jednak, że robisz to wszystko tak jak w Swingu. Powinieneś stworzyć plik widoku w JavaFX Scene Builder i dodać do niego kontroler. Wtedy pozbył byś się kilku problemów:

  1. Tabela, skoro jest dość mocno scustomizowana, powinna być osobną klasą, tak jak ExceptionAlert.
  2. Klasa Buttons wypadła by całkowicie (bardzo brzydki byt).

Oczywiście tym sposobem też się da, tylko będzie trudniej. Jeżeli się uczysz, to nawet proponuję Ci zrobić tą aplikację w sposób jakim zacząłeś, a potem przerobić to na widok + kontroler.

Ok, więc Twoja aplikacja ma 4 główne elementy:

  • Okno
  • Tablę jako explorer
  • Przyciski do nawigacji
  • ExceptionAlert do wypadków

ExceptionAlert ma dość specyficzne zadanie. Zostało wyciągnięte do osobnej klasy żebyś mógł go łatwo stworzyć. Tutaj jest dobrze.
Pozostały więc przyciski, tabela i okno. Tabela również ma osobne zadanie. Ma wyświetlić zawartość jakiegoś katalogu, obsługiwać dwukliki etc. Nie da się więc stworzyć go jedną linijką i też można wyrzucić to do osobnej klasy. A co gdybyś chciał np, przerobić to na taki ala total commander? Masz klasę - no problemo. Tworzysz drugą instancję, umieszczasz obok pierwszej i masz widok 2 katalogów. A może zakładki? Też nie ma problemu. Każda zakładka niech tworzy sobie swoją instancję. Widzisz o co mi chodzi?
Z przyciskami jest już inaczej. One się niczym specjalnym nie wyróżniają. Takie przyciski spotkamy w każdym programie. Posiadają 2 cechy. Napis i czynność do wykonania po kliknięciu. W javie, są 2 zmienne, bo czynność to nic innego jak funkcja. Dlatego przycisków nie ma sensu wywalać do osobnej klasy. Te oryginalne wystarczą.
Okno natomiast, to jest to wszytko razem złączone do kupy.

I teraz przypiszmy te właściwości do obiektów. Jeżeli wrzucisz stos do okna, to będziesz mógł się do niego odnieść z każdego miejsca. Ale co w przypadku gdy pojawi się 10 dodatkowych tabel? Przecież nie będziesz tworzył 10 dodatkowych stosów w oknie. Czyli stos czy currentPath to właściwiość właśnie tabeli. To ona wie w którym miejscu obecnie jest i w jakich miejscach była wcześniej. Powinna udostępnić metody getCurrentPath, goToPath czy goBack i goNext, ale samo zarządzanie stosem nie powinno być dostępne z zewnątrz. Do tego operacje tworzenia katalogu i usuwania też można wyrzucić do osobnej klasy, bo przecież nie jest to typowa obsługa okna.
Jeżeli kiedyś np. będziesz chciał dodać ostrzeżenie przy usuwaniu ("czy na pewno chcesz usunąć ten plik"), to nie mieszasz w oknie, tylko w obiekcie który manipuluje plikami i katalogami.

I tak np. głowne okno tworzy tabelę i 4 przyciski.

  • back - wywołuje goBack na tabeli
  • up - wywołuje goUp na tabeli
  • createDirectory - tworzy obiekt DirectoryManipulator i wywołuje na nim createDirectory(table.getCurrentPath())
  • delete - tworzy obiekt DirectoryManipulator i wywołuje na nim delete(table.getSelectedPath).

W ten sposób dojdą 2 nowe klasy specjalistyczne, a kod okna zmniejszy się do potrzebnego minimum.

Na sam koniec pewna uwaga. Nigdy nie stosuj @SuppressWarnings("unchecked"). Ukrycie ostrzeżeń nie sprawi że twój kod jest lepszy. W ten sposób po prostu olewasz problem, który dalej istnieje. To trochę tak jak ludzie się cieszą że żaden test się nie wywala, bo żadnego nie napisali ;). Jeżeli coś rzuca Ci ostrzeżeniem, to masz 2 wyjścia.

  • Poprawić tak, żeby nie rzucało
  • Za każdym razem gdy otwierasz plik patrzeć na warning i po n-tym razie gdy mocno się wkur** wrócić do punktu 1 ;).

W ukrywaniu błędów jest jeszcze jedna pułapka. Załóżmy ze coś ukryjesz, ale wiesz czemu tak się dzieje i wiesz że to nie jest żaden problem. Potem możesz coś zmienić w kodzie i niechcący dopisać coś co też rzuci warningiem, ty tego nie zauważysz, a adnotacja pozwoli to zignorować.

0

Niefajnie jednak, że robisz to wszystko tak jak w Swingu. Powinieneś stworzyć plik widoku w JavaFX Scene Builder i dodać do niego kontroler.

Nie korzystam z Buildera, jakoś nie mogłem się do niego przekonać. Możliwe, że niedługo zrobię drugie podejście, w końcu wypisywanie komponentów zajmuje zbyt długo czasu.

Tabela, skoro jest dość mocno scustomizowana, powinna być osobną klasą, tak jak ExceptionAlert.

Tak zrobię.

Klasa Buttons wypadła by całkowicie (bardzo brzydki byt).

Wcześniej znajdowała się w GUI, lecz wydawało mi się, że można ją wyciąć. Chyba tutaj tak naprawdę pojawiły się pierwsze problemy.

ExceptionAlert ma dość specyficzne zadanie. Zostało wyciągnięte do osobnej klasy żebyś mógł go łatwo stworzyć. Tutaj jest dobrze.
Pozostały więc przyciski, tabela i okno. Tabela również ma osobne zadanie. Ma wyświetlić zawartość jakiegoś katalogu, obsługiwać dwukliki etc. Nie da się więc stworzyć go jedną linijką i też można wyrzucić to do osobnej klasy. A co gdybyś chciał np, przerobić to na taki ala total commander? Masz klasę - no problemo. Tworzysz drugą instancję, umieszczasz obok pierwszej i masz widok 2 katalogów. A może zakładki? Też nie ma problemu. Każda zakładka niech tworzy sobie swoją instancję. Widzisz o co mi chodzi?

Teraz bardziej niż kiedykolwiek. Spłaszczyłem kod tylko do jednego rozwiązania. Nie zachowałem żadnej abstrakcji, pozwalającej na przyszłą rozbudowę kodu, co sprawiło, że projekt upadł zanim powstał.

Na sam koniec pewna uwaga. Nigdy nie stosuj @SuppressWarnings("unchecked").

Obiecuję, że nie będę :).

Za każdym razem gdy otwierasz plik patrzeć na warning i po n-tym razie gdy mocno się wkur** wrócić do punktu 1

Dobre rozwiązanie :D

Czeka mnie jeszcze sporo pracy nad tą umiejętnością. @krzysiek050 dziękuję Ci za poświęcony czas, konstruktywną krytykę i nieocenione słowa wskazówek i porad.

1 użytkowników online, w tym zalogowanych: 0, gości: 1