Mankamenty dziedziczenia - jaki wzorzec zastosować?

0

Witam
Zacznę może od początku. Piszę grę, w której gracz porusza statkami. W pierwotnym zamyśle istniał tylko jeden typ statku.
Był on jednocześnie encją w bazie danych (z następującymi polami: id, położenie na mapie, typ) i tworzonym z niej obiektem klasy 'Statek', w którym znajdowały się te same pola co we wcześniej wspomnianej encji + szybkość, nazwa, opis itp.
Miało to na celu ograniczenie ilości pól w bazie. Tworzenie takiego obiektu wyglądało mniej więcej tak:

Statek statek = new Statek(statekEncja)
Potrzebne pola były pobierane z encji i ustawiane w konstruktorze . Reszta była ustawiana 'na sztywno'.
Problem pojawił się w momencie, gdy wpadłem na pomysł tworzenia różnych typów statków, jak np. handlowe czy wojenne. Pierwszym odruchem było utworzenie klasy abstrakcyjnej 'Statek', w której umieściłem wszystkie wspólne pola.
Następnie utworzyłem dziedziczącą klasę 'Statek handlowy'.
I w tym miejscu właśnie pojawia się problem. W jaki sposób można dodać pola i metody (np. pojemość ładowni w statku handlowym), w ten sposób żeby statek handlowy dalej był typu 'Statek'? Dodam, że nie chodzi mi o modyfikację kodu metod, jak we wzorcu dekorator, a raczej o całościowy sposób rozwiązania tego problemu, łącznie ze sposobem zapisu do bazy danych. Mam nadzieję, że wystarczająco jasno przedstawiłem problem.
Pozdrawiam i czekam na sugestie:)

0

Sposób najprostszy. Tworzysz klasę StatekHandlowy, rozszerzającą Statek i zawierającą wszystkie pola charakterystyczne dla tego typu statku. Następnie w bazie danych tworzysz tabelę statek_handlowy, która zawiera kolumny z klasy StatekHandlowy. "Myk" polega na powiązaniu po→edzy tabelami statek i statek_handlowy. W tej drugiej tabeli kolumna id jest kluczem głównym ORAZ kluczem obcym z tabeli statek. Mowiąc prościej tabela statek i statek_handlowy mogą zostać połączone po kolumnie id.

Przykładowo

Statek

ID Nazwa
1 Aurora

Statek_handlowy

ID ładowność
1 100

i następnie piszesz sobie SQL:

Select s.id, s.nazwa, sh.ładowniść from Statek s, statek_handlowy sh where sh.id = s.id

czy jakoś tak :)

0

Idąc dalej tym tropem, ja bym umieścił w nadklasie zapytanie pobierające dane wspólne dla wszystkich statków, a w klasie StatekHandlowy pobierał zapytaniem tylko dane charakterystyczne dla statków handlowych (ładowność) ale dopiero gdy klient tych danych zażąda.

0

@lhp, to nie takie proste jak się wydaje. Potrzebujesz zachować spójność obiektu i w takim przypadku leniwe ładowanie może być źródłem kilku "ciekawych" problemów.

0

Właściwie sposób implementacji może też zależeć od dostępnego systemu zarządzania bazą danych
W niektórych tabele mogą po sobie dziedziczyć identycznie jak obiekty, w niektórych można przechowywać całe obiekty

0

Program jest webowy, pisany w Javie m.in. z u użyciem Springa. Jako ORM zastosowałem Hibernate'a.
Do tej pory w bazie była tylko jedna tabela 'Statek', w której zapisany był m.in typ statku. Z takiego obiektu (czyli klasy mapowanej w Hibernatcie o nazwie np. StatekEncja) i jego pola 'typStatku' tworzony byl obiekt 'pamięciowy', który na tej podstawie tworzył odpowiednią implementacje klasy Statek(zwykłe POJO - nic związanego bezpośrednią z bazą danych)

Statek statek;
 if (statekEncja.pobierzTyp == TypStatku.StatekHandlowy)
{
 statek = new StatekHandlowy;
}

W przypadku zastosowania zaproponowanego przez Koziołka wydaje mi się, że w bazie znalazłoby się wiele powtarzających wartości, bo w tabeli 'StatekHandlowy' zawsze pojemność będzie wynosiła np. 100. Nie mam dużego doświadczenia, więc proszę także o sugestie w tej sprawie.
Jednocześnie nie rozwiązuje to mojego problemu całkowicie.
W tym momencie mam stworzoną klasę serwisów dla jednostek, w której znajduje się metoda 'płyń', która jako argument dostaje klasę 'Statek'. Metoda ta najpierw przemieszcza statek, a następnie w przypadku gdy jest to np. port musi rozpoznać typ statku, aby móc odpowiednio zareagować. Statek handlowy wyładowuje towary, wojenny nic nie robi. W celu wyładowania towarów serwis musi "dostać się" do pojemności ładowni oraz jej wypełnienia. Niestety, takie pola i metody nie występują w interfejsie.
Jedną z możliwości wydaje mi się przeniesienie metody 'płyń' do klasy 'Statek', a następnie dekoratorem zmieniać jej implementację. Mam nadzieję, że przedstawiony przeze mnie opis problemu będzie wystarczająco jasny. Dziękuję za dotychczasowe sugestie i proszę o dalsze :)

0

hibernate dostarcza kilka fajnyc opcji: http://docs.jboss.org/hibernate/orm/3.5/reference/en/html/inheritance.html

Co do duplikacji wartości w bazie. Who cares? Będziesz chcial różnicować statki handlowe w zależności od pojemności to będzie łatwiej.

Co do serwisu to metoda plyn powinna być raczej w statku. Zaś pola mogą korzystać ze wzorca Visitor > http://en.wikipedia.org/wiki/Visitor_pattern

0

Czyli podumowując:

  • wszystkie klasy 'pamieciowe' przenieść do hibernata i tam za pomocą dziedziczenia odtworzyć całą strukturę
  • pola dodawać do nich za pomocą wizytatora

Czy o takie coś chodziło?
Może powtarzające pola oznaczać jako @Transient?

1

Pytanie po co chcesz je oznaczać w ten sposób? Miejsca w bazie ni zaoszczędzisz dużo, a tylko będziesz miał bardziej poszyty kod.

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