Problem z prawidłowym rozplanowaniem aplikacji - diagram klas pajęczyna

0

Kończę pisać aplikację do nauki słów. Używam JavaFX (czyli MVC, na razie logika leży w kontrolerach, ale nie w tym problem). Kłopot polega na tym, że ten kod jakoś mnie nie zadowala, mam na myśli relacje klas i obiektów. Nie przekonują mnie. Nie mam trudności z jego rozwijaniem, dodawaniem nowych rzeczy, nie ma bugów, ale czuję, że to nie jest tak napisane jak powinno być. Diagram klas jak widać "rozbudowany", nie wygląda jak drzewko. Czy to normalne? Nie jest to duża ani skomplikowana aplikacja :| Baza też jest prosta, mam table users (klasa User), words (klasa Record - nazwa też do poprawy), trzecią można pominąć. Jeżeli jeszcze są potrzebne jakieś informacje mogę dopisać :)
Ostatnio robiłem trochę poprawek, kod: https://github.com/Potat0x/Vocab-Flash/tree/master/src/vocabflash
Co byście na to poradzili?

1

Architektura aplikacji powinna wyglądać jak skierowany graf acykliczny, gdzie kierunek zależności wskazuje na co raz bardziej abstrakcyjne byty. U ciebie nie ma modułów czy podziałów na warstwy, masz wrzucone wszystko w jeden pakiet, co dałoby rade i tak ogarnąć, ale patrząc na to nic nie widać na pierwszy rzut oka. Może spróbuj zrobić z tego MVC? Podziel na pakiety etc

BTW: https://github.com/Potat0x/Vocab-Flash/blob/master/src/vocabflash/ExportingController.java#L89 poczytaj clean code i nie przejmuj się architekturą

0

O podziale na pakiety nawet nie myślałem. Mógłbyś zaproponować jakiś mały przykład? Chociaż nie sądzę, żeby na razie mi to pomogło.

Może opiszę kilka problemów.

  1. Mam LoginController, który po udanym zalogowaniu uruchamia okno należące do ManagementController. Gdy użytkownik chce się wylogować (z poziomu ManagementController) muszę jakoś wrócić do okna logowania. Teraz robię to tak, że w tym celu kontroler logowania przekazuje siebie przez konstruktor do ManagementController. I już mamy cykl.

  2. Klasa DatabaseConnector. Zajmuje się zarówno listami słów, dodawaniem użytkowników, zmianą ich ustawień itp. A gdyby zrobić, że logowanie, zmiana ustawień, zarządzanie słowami mają swoje dedykowane "connectory"?

  3. Słynne singletony. Przed ostatnimi poprawkami trochę ich miałem. Aktualnie został tylko jeden, który służy do tłumaczenia elementów interfejsu (czyli musi być odczytywany w wielu miejscach). Wydaje mi się, że jest to ok. Czy nie?

hcubyc napisał(a):

BTW: https://github.com/Potat0x/Vocab-Flash/blob/master/src/vocabflash/ExportingController.java#L89 poczytaj clean code i nie przejmuj się architekturą

Hmm. Chodzi o długość funkcji, ilość zagnieżdżeń typów, czy jeszcze coś innego?

1

Hmm. Chodzi o długość funkcji, ilość zagnieżdżeń typów, czy jeszcze coś innego?

Dokładnie tak. W jednej metodzie dzieje się bardzo dużo rzeczy, można by to było porozbijać

Mam LoginController, który po udanym zalogowaniu uruchamia okno należące do ManagementController. Gdy użytkownik chce się wylogować (z poziomu ManagementController) muszę jakoś wrócić do okna logowania. Teraz robię to tak, że w tym celu kontroler logowania przekazuje siebie przez konstruktor do ManagementController. I już mamy cykl.

Nie bardzo znam technologię i jej możliwości, ale czy nie ma możliwości żeby kontroler zwracał widok lub identyfikator widoku? I do tego były jakiś mega-kontroler jakby go nie nazwać, który by nimi zarządzał tak żeby kontrolery nie musiały nic wiedzieć o sobie? Więc kontroler od loginu obsługiwałby tylko okno logowania, ale nie zawierałby referencji do drugiego kontrolera tylko zwracał nawet Stringa z ID widoku, i byłaby rzecz ponad nim (inny kontroler, zarządca, whatever), który by się zajmował tylko przekierowaniami między kontrolerami?

Klasa DatabaseConnector. Zajmuje się zarówno listami słów, dodawaniem użytkowników, zmianą ich ustawień itp. A gdyby zrobić, że logowanie, zmiana ustawień, zarządzanie słowami mają swoje dedykowane "connectory"?

Tak, to może być wzorzec repository. Prawdopodobnie kod też będzie dało się tak podzielić, żeby oddzielić te rzeczy od siebie. https://github.com/spring-projects/spring-petclinic tu masz bardzo prosty przykład podziału na pakiety

Słynne singletony. Przed ostatnimi poprawkami trochę ich miałem. Aktualnie został tylko jeden, który służy do tłumaczenia elementów interfejsu (czyli musi być odczytywany w wielu miejscach). Wydaje mi się, że jest to ok. Czy nie?

Z tego co piszesz to tak. Ważne żeby nie zmieniał stanu przez cały cykl życia aplikacji - wtedy nie trzeba się martwić wielowątkowością. Mógłbyś też tworzyć nową instancję obiektu za każdym razem jak chcesz tłumaczyć, ale dla mnie obydwa podejścia są spoko, więc jak ci wygodniej

1
Potat0x napisał(a):

O podziale na pakiety nawet nie myślałem. Mógłbyś zaproponować jakiś mały przykład? Chociaż nie sądzę, żeby na razie mi to pomogło.

Typowy błąd początkującego.

Może opiszę kilka problemów.

  1. Mam LoginController, który po udanym zalogowaniu uruchamia okno należące do ManagementController. Gdy użytkownik chce się wylogować (z poziomu ManagementController) muszę jakoś wrócić do okna logowania. Teraz robię to tak, że w tym celu kontroler logowania przekazuje siebie przez konstruktor do ManagementController. I już mamy cykl.

Czy one naprawdę muszą o sobie wzajemnie wiedzieć? Niech cała komunikacja między nimi odbędzie się tak, aby te dwa puzzle można było niezależnie od siebie wymienić.

  1. Klasa DatabaseConnector. Zajmuje się zarówno listami słów, dodawaniem użytkowników, zmianą ich ustawień itp. A gdyby zrobić, że logowanie, zmiana ustawień, zarządzanie słowami mają swoje dedykowane "connectory"?

Jak widzę "database Conector" to widzę coś co mi nawiąże połączenie z bazą. Manipulacje bazą jakoś mi się już tutaj z tym nie asocjują.

  1. Słynne singletony. Przed ostatnimi poprawkami trochę ich miałem. Aktualnie został tylko jeden, który służy do tłumaczenia elementów interfejsu (czyli musi być odczytywany w wielu miejscach). Wydaje mi się, że jest to ok. Czy nie?

Jeśli uznajesz, że są powody by go pozostawić, to pewnie coś za tym przemawia. Umiesz nam te powody przedstawić? Jeśli tak, to zapewne coś jest na rzeczy. Jeśli nie, to masz problem.

hcubyc napisał(a):

BTW: https://github.com/Potat0x/Vocab-Flash/blob/master/src/vocabflash/ExportingController.java#L89 poczytaj clean code i nie przejmuj się architekturą

Hmm. Chodzi o długość funkcji, ilość zagnieżdżeń typów, czy jeszcze coś innego?

Jeśli przypatrując się danej funkcji, nie jesteś w ciągu 10 sekund dojść co się w niej dzieje, i nie jest to jakiś niskopoziomowy sterownik, czy wycinek kernela, to oznacza, że kod jest niejasny - za długi i i realizowane są w nim rzeczy, które należałoby wydzielić do osobnych funkcji.

0

Dzięki za odpowiedzi. Jedźmy dalej :)

Sklonowałem sobie petclinic. Patrzę na diagramy, dobrze to wygląda. Chciałbym osiągnąć podobny efekt.
Rozbiłem tą długą metodę, a nawet przeniosłem ją do właściwej klasy, wywaliłem też kilka linijek, które w ogóle nie były tam potrzebne. Chociaż w 10 sekund raczej nie da się ogarnąć co tam się dzieje.
Z tym singletonem, rzeczywiście powinno być ok.
Usunąłem (zamiast funkcji show() użyłem showAndWait()) cykliczną zależność LoginController i ManagementController. Teraz jest jednostronna: Login "wywołuje" Management. Management z kolei ma w sobie inne kontrolery. Cyklicznych zależności nie ma. Mega-kontroler to lepsze podejście?

Dalszy ciąg problemów pierwszego świata.

  1. Dodałem klasę WordBase, która komunikuje się z bazą w kontekście słów. Powstała głównie przez Ctrl+V DatabaseConnectora i wycięcie nietematycznych funkcji (czyli kod wymiatał...). Więc w tych dwóch klasach na razie powtarza się: obiekt Connection, timeout, id użytkownika i kilka funkcji.
    Rozwiązać to przez dziedziczenie, czy stworzenie nowej klasy (o nazwie DatabaseConnector ;) ) która będzie odpowiedzialna tylko za łącznie z bazą, a jej obiekt przekazywany przez konstruktory? W obu przypadkach i tak muszę coś przekazać. Albo ten obiekt databaseConnector, albo suche parametry połączenia (jutro jeszcze się nad tym zastanowię).

  2. Klasa User, w której przechowuję login, id, ustawienia (prawie cały wiersz tabeli). Rzecz trywialna, ale jak to zrobić bez cyklicznej zależności między klasą User, a klasą, która go wczytuje z bazy (czyli zwraca User), aktualizuje (przyjmuje User jako argument) itd. A może już przesadzam?

  3. W dwóch różnych klasach (kontrolery) muszę stworzyć okno. Mam do tego funkcję statyczną, która siedzi w głównej klasie aplikacji. Osobna klasa dla niej to dobry pomysł? Gdyby był już wyżej wspomniany mega-kontroler, tego problemu by nie było.

To jeszcze nie wszystko, ale na razie tyle wystarczy :D

0
Potat0x napisał(a):

Dalszy ciąg problemów pierwszego świata.

  1. Dodałem klasę WordBase, która komunikuje się z bazą w kontekście słów. Powstała głównie przez Ctrl+V DatabaseConnectora i wycięcie nietematycznych funkcji (czyli kod wymiatał...). Więc w tych dwóch klasach na razie powtarza się: obiekt Connection, timeout, id użytkownika i kilka funkcji.

Czyli masz duplikaty kodu jednym słowem?

Rozwiązać to przez dziedziczenie, czy stworzenie nowej klasy (o nazwie DatabaseConnector ;) ) która będzie odpowiedzialna tylko za łącznie z bazą, a jej obiekt przekazywany przez konstruktory? W obu przypadkach i tak muszę coś przekazać. Albo ten obiekt databaseConnector, albo suche parametry połączenia (jutro jeszcze się nad tym zastanowię).

Suche parametry zmniejszą zależność od reszty kodu. Co może być dobre jeśli istotą programu są operacje na danych (które powinny być ze sobą bliżej połączone), a nie mechanizmy łączenia z bazą? W skrócie - logika i hierarchia kals przetwarzania danych spokojnie może być ze sobą ściślej powiązana niż elementy takie jak wczytywanie i zapisywanie do bazy danych. Jeśli te mechanizmy odczytu/zapisu będą lepiej odseparowane, to będą łątwo wymienialne - np. kiedyś będzie łatwiej wymienić je na zapis do czegoś zupełnie innego niż relacyjna baza danych. Ale to w sumie może być na wyrost w aplikacji, która zostanie porzucona niedługo po jej ukończeniu i niewielka szans jest na jej przeszczep na inne środowisko niż relacyjna baza danych, czy nawet zmiana typu tej bazy danych z przykłądowo MySQL na PostgreSQL.

  1. Klasa User, w której przechowuję login, id, ustawienia (prawie cały wiersz tabeli). Rzecz trywialna, ale jak to zrobić bez cyklicznej zależności między klasą User, a klasą, która go wczytuje z bazy (czyli zwraca User), aktualizuje (przyjmuje User jako argument) itd. A może już przesadzam?

Ale po co mieszać Usera i konfigurację? Jeśli program wykonuje operacje na jednym i drugim niezależnie? Chyba, że tam gdzie przetwazrasz dane z usera przetwarzasz zawsze dane konfiguracji.

  1. W dwóch różnych klasach (kontrolery) muszę stworzyć okno. Mam do tego funkcję statyczną, która siedzi w głównej klasie aplikacji. Osobna klasa dla niej to dobry pomysł? Gdyby był już wyżej wspomniany mega-kontroler, tego problemu by nie było.

Patrz przypadek bazy danych. Zamierzasz kiedykolwiek wymieniać system wyświetlania okien? W sumie czy widzisz jakieś podstawy traktowania okien inaczej niż bazy danych? Co myślisz na ten temat? Jestem ciekaw.

0
Błękitny Kot napisał(a):

Czyli masz duplikaty kodu jednym słowem?

Chwilowo tak.
Jeżeli chodzi o suche parametry, to co mnie odstrasza: trzeba przerzucać dużo argumentów do funkcji. W ogóle gdzieś czytałem, że funkcje powinny być co najwyżej jednoargumentowe, ale jeszcze nie jestem na takim poziomie wtajemniczenia :P

Ale po co mieszać Usera i konfigurację?

Czyli powinna być osobna klasa na użytkownika i osobna na ustawienia?
Jako użytkownika rozumiem wiersz z tabeli users i rowid. Tabela wygląda tak:
username, password, register_date, last_training_date, total_trainings, gui_lang, base_lang, foreign_lang.
Ostatnie trzy kolumny (i hasło) to ustawienia.
Mam jedną klasę, w której potrzebuję całego wiersza. To klasa odpowiedzialna za za komunikację z bazą.

  • w oknie zmiany ustawień odczytuję i zmieniam tylko ustawienia
  • zaraz po zalogowaniu muszę odczytać gui_lang, żeby przetłumaczyć interfejs
  • na potrzeby okna dodawania słów muszę odczytać jeszcze dwa ostatnie pola.

Patrz przypadek bazy danych. Zamierzasz kiedykolwiek wymieniać system wyświetlania okien? W sumie czy widzisz jakieś podstawy traktowania okien inaczej niż bazy danych? Co myślisz
na ten temat? Jestem ciekaw.

Systemu wyświetlania okien nie zamierzam zmieniać. Jeżeli chodzi o bazę danych, to możliwe że dodam do aplikacji współpracę z Oraclem. Więc jest tu jakaś podstawa do innego traktowania.

0
Potat0x napisał(a):
Błękitny Kot napisał(a):

Czyli masz duplikaty kodu jednym słowem?

Chwilowo tak.
Jeżeli chodzi o suche parametry, to co mnie odstrasza: trzeba przerzucać dużo argumentów do funkcji. W ogóle gdzieś czytałem, że funkcje powinny być co najwyżej jednoargumentowe, ale jeszcze nie jestem na takim poziomie wtajemniczenia :P

I bardzo dobrze, bo to poziom na którym przychodzą muskularni panowie z dziwnym kubraczkiem, który czemuś chcą ci założyć i dorzucają gratis pokoik z pluszowymi ścianami.

Ale po co mieszać Usera i konfigurację?

Czyli powinna być osobna klasa na użytkownika i osobna na ustawienia?

Wszystko zależy od kontekstu. Jeśli to aplikacja "jednoosobowa", to nie widzę na poziomie aplikacji sensu "ciągania" danych o konfiguracji. Konfiguracja wpływa jedynie na bieżący stan programu. Tutaj w sumie pojawia się pytanie, czy jest sens w ogóle abstrahować takie rzeczy jak user, czy konfiguracja bardziej niż obsługę wyświetlania okien? Potrafisz podać uzasadnienie dla ewentualnego "awansu" do odzdzielnej klasy danych użytkownika i/lub konfiguracji?

Patrz przypadek bazy danych. Zamierzasz kiedykolwiek wymieniać system wyświetlania okien? W sumie czy widzisz jakieś podstawy traktowania okien inaczej niż bazy danych? Co myślisz
na ten temat? Jestem ciekaw.

Systemu wyświetlania okien nie zamierzam zmieniać. Jeżeli chodzi o bazę danych, to możliwe że dodam do aplikacji współpracę z Oraclem. Więc jest tu jakaś podstawa do innego traktowania.

To istotnie brzmi jak sensowna podstawa do wydzielenia jako osobnego modułu.

0
Mistrzowski Ogórek napisał(a):

I bardzo dobrze, bo to poziom na którym przychodzą muskularni panowie z dziwnym kubraczkiem, który czemuś chcą ci założyć i dorzucają gratis pokoik z pluszowymi ścianami.

:D

Wszystko zależy od kontekstu. Jeśli to aplikacja "jednoosobowa", to nie widzę na poziomie aplikacji sensu "ciągania" danych o konfiguracji. Konfiguracja wpływa jedynie na bieżący stan programu. Tutaj w sumie pojawia się pytanie, czy jest sens w ogóle abstrahować takie rzeczy jak user, czy konfiguracja bardziej niż obsługę wyświetlania okien? Potrafisz podać uzasadnienie dla ewentualnego "awansu" do odzdzielnej klasy danych użytkownika i/lub konfiguracji?

Aplikacja jest wieloosobowa, użytkownicy mogą się rejestrować. Konfiguracja nie wpływa tylko na bieżący stan. Zapisuje różne rzeczy, aby ułatwić użytkownikom życie. Automatycznie ustawiać język GUI po zalogowaniu, zapamiętać język tłumaczenia, żeby nie było trzeba za każdym razem wybierać z comboboxa. Do tego w kilku różnych klasach potrzebny mi dostęp do tych informacji.

0
Potat0x napisał(a):
Mistrzowski Ogórek napisał(a):

I bardzo dobrze, bo to poziom na którym przychodzą muskularni panowie z dziwnym kubraczkiem, który czemuś chcą ci założyć i dorzucają gratis pokoik z pluszowymi ścianami.

:D

Wszystko zależy od kontekstu. Jeśli to aplikacja "jednoosobowa", to nie widzę na poziomie aplikacji sensu "ciągania" danych o konfiguracji. Konfiguracja wpływa jedynie na bieżący stan programu. Tutaj w sumie pojawia się pytanie, czy jest sens w ogóle abstrahować takie rzeczy jak user, czy konfiguracja bardziej niż obsługę wyświetlania okien? Potrafisz podać uzasadnienie dla ewentualnego "awansu" do odzdzielnej klasy danych użytkownika i/lub konfiguracji?

Aplikacja jest wieloosobowa, użytkownicy mogą się rejestrować. Konfiguracja nie wpływa tylko na bieżący stan. Zapisuje różne rzeczy, aby ułatwić użytkownikom życie. Automatycznie ustawiać język GUI po zalogowaniu, zapamiętać język tłumaczenia, żeby nie było trzeba za każdym razem wybierać z comboboxa. Do tego w kilku różnych klasach potrzebny mi dostęp do tych informacji.

Wieloosobowa, czyli okreś w jakim sensie:

  • korzysta z niej wielu użytkowników, ale tylko jeden w danymc czasie, nie można przełączać użytkowników "w locie"
  • korzysta z niej wielu użytkowników, ale tylko jeden w danymc czasie, można przełączać użytkowników "w locie"
  • jest to aplikacja klient - serwerz wieloma uzytkownikami w tym samym czasie morzystającymi z aplikacji i jednego serwera obsługującego bazę danych
    -inne warianty

Bo inego podejścia wymaga pierwsza sytuacja, a innego druga, a kompletnie odmiennego trzecia.
Zastanów się jakie są argumenty za wychodzeniem poza minimalny potrzebny do zrealizowania zadania poziom abstrakcji. Jeśli nie ma to jaki jest to poziom. Jeśli są, to jestem ciekaw uzasadnienia.

0

Wciąż nie masz podziału na pakiety - IMHO przydałby się, bo teraz mimo że nie masz zależności między poszczególnymi klasami to i tak wygląda jak jeden moduł - otwierasz katalog i jest tam po prostu wszystko ;)

Klasa User, w której przechowuję login, id, ustawienia (prawie cały wiersz tabeli). Rzecz trywialna, ale jak to zrobić bez cyklicznej zależności między klasą User, a klasą, która go wczytuje z bazy (czyli zwraca User), aktualizuje (przyjmuje User jako argument) itd. A może już przesadzam?

Metoda małych kroków. Jeżeli masz na myśli klasy User i DatabaseConnector to nie ma między nimi cyklicznej zależności. User nic nie wie o DC, za to DC wie o User - zależnośc w jedną stronę. Co więcej to DC zależy od user, czyli klasa mniej abstrakcyjna zależy od klasy bardziej abstrakcyjnej, czyli zależnośc w dobrym kierunku.

stworzenie nowej klasy (o nazwie DatabaseConnector ;) ) która będzie odpowiedzialna tylko za łącznie z bazą, a jej obiekt przekazywany przez konstruktory? W obu przypadkach i tak muszę coś przekazać. Albo ten obiekt databaseConnector, albo suche parametry połączenia

Podoba mi się to podejście. Pomysl o tym w ten sposób, że parametry połączenia to coś o czym powinien wiedzieć obiekt odpowiedzialny za połączenie i żaden inny, przepychanie tego parametrami to nie jest najlepsze wyjście. Dziedziczenia też bym unikał, tylko zastosował wstrzykniecie zaleznosci przez konstruktor, bo te klasy będą korzystać z połączenia z bazą danych, a nie będa kolejną klasą łącząca się z bazą danych, tylko np. bardziej rozbudowaną - i to jest kwestia interpretacji, ale dziedziczneie jest mocniejsza relacją, lepiej jej unikać.

W dwóch różnych klasach (kontrolery) muszę stworzyć okno. Mam do tego funkcję statyczną, która siedzi w głównej klasie aplikacji. Osobna klasa dla niej to dobry pomysł? Gdyby był już wyżej wspomniany mega-kontroler, tego problemu by nie było.

spróbuj pierwszy sposób i drugi, może podczas pisania któryś bardziej ci się spodoba ;) Podaj link konkretnie do tego miejsca to będzie łatwiej doradzić

0

Jest to klient, który łączy się bezpośrednio z bazą.
Wieloosobowa w sensie:

  • ogólnie wielu użytkowników
  • możliwość zmiany użytkownika w locie, podczas działania aplikacji (wylogowywanie i logowanie)
  • aktualnie w danym czasie z aplikacji może korzystać tylko jeden użytkownik (wbudowana baza), ale gdyby to przenieść na Oracle, Postgresa itd. to nie ma przeszkód, aby wielu użytkowników korzystało z aplikacji w tym samym czasie. Użytkownicy nawet nie modyfikują żadnych współdzielonych danych.

Zastanów się jakie są argumenty za wychodzeniem poza minimalny potrzebny do zrealizowania zadania poziom abstrakcji.

Poza minimalny potrzebny? Zapewne takich nie ma ;)

Jeśli nie ma to jaki jest to poziom.

Chodzi o to czy minimalny, czy nie? Wydaje mi się, że w tym przypadku moje podejście jest ok.

//
@hcubyc
Jutro będę miał czas, zacznę coś uskuteczniać w kodzie.

Jeżeli masz na myśli klasy User i DatabaseConnector to nie ma między nimi cyklicznej zależności. User nic nie wie o DC, za to DC wie o User - zależnośc w jedną stronę. Co więcej to DC zależy od user, czyli klasa mniej abstrakcyjna zależy od klasy bardziej abstrakcyjnej, czyli zależnośc w dobrym kierunku.

Rzeczywiście, jest tak jak piszesz. Pewnie wzięło się to stąd, że myślałem nad takim podejściem, że User ma w sobie schowane operacje na bazie i na przykład user.setPassword() by automatycznie zmieniało hasło w bazie.

spróbuj pierwszy sposób i drugi, może podczas pisania któryś bardziej ci się spodoba ;) Podaj link konkretnie do tego miejsca to będzie łatwiej doradzić

Raczej zrobię tą klasę, zwłaszcza że będę w przyszłości jeszcze coś pisał w JavaFX i mi się to przyda.
Tu jest ta funkcja.
https://github.com/Potat0x/Vocab-Flash/blob/master/src/vocabflash/VocabFlash.java#L18
a tu jej używam
https://github.com/Potat0x/Vocab-Flash/blob/master/src/vocabflash/ManagementController.java#L48
https://github.com/Potat0x/Vocab-Flash/blob/master/src/vocabflash/LoginController.java#L104

0

Nie możesz wstrzyknąć okna bezpośrednio do kontrolera? Jeżeli bys kiedykolwiek chciał przetestowac unitowo kontrolery co samo w sobie może nie być najlepszym pomysłem - ale warto o to dbać - to nie będziesz mógł - bo nie wstawisz zaslepki/mocka okna przez statyczną funkcję

0
hcubyc napisał(a):

Nie możesz wstrzyknąć okna bezpośrednio do kontrolera?

Pokombinuję z tym.

Zrobiłem akcję z DatabaseConnectorem i klasami, które go używają. Wcześniej zapomniałem, że przecież SQL też zależy od bazy (akurat tu zapytania są na tyle proste, że nie będzie różnic między bazami). Jeżeli chciałbym zrobić aplikację przenośną między bazami i odporną na te różnice, to jak bym musiał zorganizować stringi zapytań?

Po zmianach:

class DatabaseConnector {

    private Connection connection;
    private final int timeout = 3;

    Statement createStatement() throws SQLException {
        Statement statement = connection.createStatement();
        statement.setQueryTimeout(timeout);
        return statement;
    }

    void connect() {
        final String DB_DRIVER = "org.sqlite.JDBC";
        final String DB_URL = "jdbc:sqlite:baza.db";
        try {
            Class.forName(DB_DRIVER);
            connection = DriverManager.getConnection(DB_URL);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }

    void disconnect() {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

Przykładowe użycie connectora.

class UserBase {

    DatabaseConnector databaseConnector;
    //...
    void updateUser(User user) {
        databaseConnector.connect();
        try (Statement statement = databaseConnector.createStatement()) {
            statement.executeUpdate(
                    String.format("UPDATE users SET last_training_date = '%s', blabla",
                            user.lastTrainingDate, ...));
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            databaseConnector.disconnect();
        }
    }
}

Analogiczna klasa dla słów to UserBase.
Wstawiłem diagram (bez nieistotnych klas). Co o nim myślicie?

0

UserBase? Po kiego kija? Mnożenie bytów ponad miarę.

Do twojego pytania o bazy - ten temat porusza największą piętę achillesową OOPu, której nawet najwięksi zaeloci tego paradygmatu nie są w stanie zakrzyczeć - faktu, że relacyjne bazy danych dość kijowo się integrują z obiektowością. więc komunikację lepiej oprzeć na tym co jest potrzebne dla programu jakby to były surowe dane, nie wiersze i kolumny z tabeli.

0
Potat0x napisał(a):

Wstawiłem diagram (bez nieistotnych klas). Co o nim myślicie?

Ile jest tych "nieistotnych klas"? Jeśli coś koło tego co już jest lub więcej to dla mnie strzelanie z armaty do komara.

0
Brunatny Lew napisał(a):

więc komunikację lepiej oprzeć na tym co jest potrzebne dla programu jakby to były surowe dane, nie wiersze i kolumny z tabeli.

Co masz na myśli?

Ile jest tych "nieistotnych klas"? Jeśli coś koło tego co już jest lub więcej to dla mnie strzelanie z armaty do komara.

3 klasy: "animację" kolorów, enuma używanego przez jedną klasę i klasę tłumaczenia GUI.

0
Potat0x napisał(a):
Brunatny Lew napisał(a):

więc komunikację lepiej oprzeć na tym co jest potrzebne dla programu jakby to były surowe dane, nie wiersze i kolumny z tabeli.

Co masz na myśli?

aby nie było widać poza tym miejscami gdzie jest to absolutnie niezbędne, że te dane pochodzą z basy sql.

Ile jest tych "nieistotnych klas"? Jeśli coś koło tego co już jest lub więcej to dla mnie strzelanie z armaty do komara.

3 klasy: "animację" kolorów, enuma używanego przez jedną klasę i klasę tłumaczenia GUI.

IMHO przeginasz(klasa do animowania kolorów?????), ale jak masz argumentację za istnieniem każdej z nich, to może coś jest na rzeczy

0
Brunatny Lew napisał(a):

aby nie było widać poza tym miejscami gdzie jest to absolutnie niezbędne, że te dane pochodzą z basy sql.

Aktualnie u mnie widać to w DatabaseConnector (obiekt Connection) i klasach WordBase i UserBase (są tam zapisane zapytania). To za dużo?

IMHO przeginasz(klasa do animowania kolorów?????), ale jak masz argumentację za istnieniem każdej z nich, to może coś jest na rzeczy

IMHO tu nie przeginam ;) https://github.com/Potat0x/Vocab-Flash/blob/master/src/vocabflash/ColorCycler.java

1
Brunatny Lew napisał(a):

UserBase? Po kiego kija? Mnożenie bytów ponad miarę.

Do twojego pytania o bazy - ten temat porusza największą piętę achillesową OOPu, której nawet najwięksi zaeloci tego paradygmatu nie są w stanie zakrzyczeć - faktu, że relacyjne bazy danych dość kijowo się integrują z obiektowością. więc komunikację lepiej oprzeć na tym co jest potrzebne dla programu jakby to były surowe dane, nie wiersze i kolumny z tabeli.

Czy ja wiem, jeżeli podzieli na moduły to bedzie pasować jak ulał zakładając, że user i word to będą osobne moduły

Jeżeli chciałbym zrobić aplikację przenośną między bazami i odporną na te różnice, to jak bym musiał zorganizować stringi zapytań?

Wystaw interfejs i niech wszystkie klasy korzystają z interfejsu. Interfejs operuje na klasach biznesowych, np metoda void updateUser(User user) jest bardzo ok - przyjmujesz obiekt jako argument, a nie zapytanie SQLowe. Jeżeli kiedykolwiek będziesz chciał zmienić bazę to jedyna zmiana to podmian implementacji

0
Potat0x napisał(a):
Brunatny Lew napisał(a):

aby nie było widać poza tym miejscami gdzie jest to absolutnie niezbędne, że te dane pochodzą z basy sql.

Aktualnie u mnie widać to w DatabaseConnector (obiekt Connection) i klasach WordBase i UserBase (są tam zapisane zapytania). To za dużo?

Opisz nam we w miarę prostych słowach jak i zamiar stoi za nimi.

IMHO przeginasz(klasa do animowania kolorów?????), ale jak masz argumentację za istnieniem każdej z nich, to może coś jest na rzeczy

IMHO tu nie przeginam ;) https://github.com/Potat0x/Vocab-Flash/blob/master/src/vocabflash/ColorCycler.java

Gdzie to siedzi w hierarchi? IMHO to mniej więcej zbliżony poziom "ważności" co wyświetlanie okienek i w miarę blisko powinien tego leżeć. Czy tak jest?

0

Opisz nam we w miarę prostych słowach jak i zamiar stoi za nimi.

DatabaseConnector - nawiązuje i zamyka połączenia z bazą..
WordBase - służy do pracy na słowach w kontekście bazy danych - ładowanie z bazy słów, dodawanie, usuwanie, aktualizowanie.
UserBase - analogicznie jak WordBase, ale dotyczy użytkowników, czyli wczytywanie użytkownika i jego właściwości, zmiana hasła, ...

Gdzie to siedzi w hierarchi? IMHO to mniej więcej zbliżony poziom "ważności" co wyświetlanie okienek i w miarę blisko powinien tego leżeć. Czy tak jest?

Poziom "ważności" raczej mniejszy, to tylko wodotrysk i można by się bez niego obejść. Leży raczej tam gdzie powinno.

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