Autor Wątek: zmienne w klasie bazowej nie uzywane w klasie pochodnej-czy to błąd?  (Przeczytany 1546 razy)

Offline jjoker8

  • Użytkownik

# Kwiecień 19, 2012, 12:36:41
Cześć, mam takie pytanie: Załóżmy że mam klasę bazową i z niej dziedziczy 10 klas pochodnych, z czego 2 nie używają pewnych funkcji klasy bazowej(metod, zmiennych). Czy jest to błąd projektowy?  Czy jak sami projektujecie kod to wychodzą wam takie rzeczy? chodzi o to że po pewnym czasie postanowiłem dopisać te 2 klasy (to się chyba zdarza dość często)
« Ostatnia zmiana: Kwiecień 19, 2012, 12:46:40 wysłana przez jjoker8 »

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Kwiecień 19, 2012, 12:54:17
Cytuj
Czy jest to błąd projektowy?
Ani trochę.

Cytuj
Czy jak sami projektujecie kod to wychodzą wam takie rzeczy?
Oczywiście. W zasadzie to jest norma.

Offline koirat

  • Użytkownik

# Kwiecień 19, 2012, 14:22:27
Moim zdaniem to nie jest dobra praktyka, jeśli czegoś nie używasz w klasie to znaczy iż klasa ma obniżoną  spójność (cohesion), tzn. metody w tej klasie nie są ze sobą powiązane (w języku trzepakowym oznacza to iż zaczyna się tam robić burdel który może być ciężko ogarnąć). Albo powinieneś zmienić dziedziczenie albo(i) wprowadzić dodatkowe klasy pomocnicze, oczywiście takie rozwiązanie również niesie ze sobą konsekwencje, dlatego musisz znaleźć złoty środek.

Tak jak sami projektujemy kod to też nam się to zdarza ale raczej powinniśmy się starać tego unikać. Jęsli był bym przy początku projektowania to raczej bym to zmienił, jeśli już na zaawansowanym etapie i nic by się nie dało zrobić to mógł bym to zostawić.

Nie wiem czy znana jest ci zasada Liskov Substitution, z tego co piszesz to prawdopodobnie ją łamiesz.

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Kwiecień 19, 2012, 16:15:09
Cytuj
Tak jak sami projektujemy kod to też nam się to zdarza ale raczej powinniśmy się starać tego unikać.
Zapomniałeś chyba o jednym przypadku: metody pomocnicze.

Przykład: mamy sobie ogólną klasę KontrolkaMojegoGUI, która ma metody typu WymuśOdrysowanie, PrzesuńDo, itp, itd. Jeżeli teraz zrobisz klasę pochodną PrzyciskMojegoGUI, to raczej nie będzie on wywoływał metody PrzesuńDo, bo sam z siebie nie ma zamiaru się przesuwać.

Offline koirat

  • Użytkownik

# Kwiecień 19, 2012, 17:40:49
Ale nie rozumiem cię tu, z tym przykładem. KontrolkaMojegoGUI oraz PrzyciskMojegoGUI, a dlaczego niby KontrolkaMojegoGui ma zamiar przesuwać się sama z siebie ?

Jeśli już chcesz mieć taki podział kontrolek to powinieneś zrobić inne dziedziczenie, jako głowna klasa powinna być KontrolkaMojegoGUI a po niej dziedziczone powinny być KontrolkaMojegoGUIRuchoma oraz KontrolkaMojegoGUINieruchoma. Albo zrobić po prostu osobny interfejs dla ruszania kontrolek który będzie dziedziczony dodatkowo jeśli kontrolka ma się ruszać.
« Ostatnia zmiana: Kwiecień 19, 2012, 18:00:54 wysłana przez Xirdus »

Offline jjoker8

  • Użytkownik

# Kwiecień 19, 2012, 17:41:32
no tak, ale właściwie chodzi mi o sytuację, gdzie np. robię manager obiektów do gry- w kontenerze mam wskaźniki na obiekty klasy bazowej. Powiedzmy że mam 2 klasy pochodne- kula i człowiek. klasa bazowa ma metody takie jak go_left(), go_right(), jump(), których kula nie używa, ale ponieważ dostęp do obiektu kula będzie realizowany przez wskaźnik na obiekt klasy bazowej może zostać przypadkiem wywołana funkcja go_left() dla obiektu typu kula: Obiekty[1]->go_left() (gdzie Obiekty to vector<IObiekt*> Obiekty).
Z drugiej strony teraz jeśli chciałbym dodać klasę kula z poprawną hierarchią to bym musiał z klasy bazowej wywalić wszystko co kuli nie dotyczy i przenieść klasę niżej.

Offline koirat

  • Użytkownik

# Kwiecień 19, 2012, 18:30:19
Ale to w tym momencie widzisz chyba iż masz tu problem z designem  (wspominałem wcześniej o Liskov substitution). Jeżeli coś się ma przypadkiem wywołać to może lepiej żeby ci się program wysypał :P.

Z drugiej strony teraz jeśli chciałbym dodać klasę kula z poprawną hierarchią to bym musiał z klasy bazowej wywalić wszystko co kuli nie dotyczy i przenieść klasę niżej.

Takie problemy to powód dlaczego powinno się projektować zanim zacznie się programować. Ogólnie naprawianie błędów podczas projektowania jest X razy tańsze niż ich naprawa podczas konstruowania.

Offline jjoker8

  • Użytkownik

# Kwiecień 19, 2012, 18:47:53
Przyznaję że to mój pierwszy "większy" "projekt", przy pisaniu snake'a problemu nie było  :)

Offline Kos

  • Użytkownik
    • kos.gd

# Kwiecień 20, 2012, 09:48:58
Jak chcesz mieć dobry design to najlepiej wcale nie dziedzicz. Same problemy z tym są. :)

Offline Xion

  • Redaktor
    • xion.log

# Kwiecień 20, 2012, 10:52:42
Cytuj
Przykład: mamy sobie ogólną klasę KontrolkaMojegoGUI, która ma metody typu WymuśOdrysowanie, PrzesuńDo, itp, itd. Jeżeli teraz zrobisz klasę pochodną PrzyciskMojegoGUI, to raczej nie będzie on wywoływał metody PrzesuńDo, bo sam z siebie nie ma zamiaru się przesuwać.
Sądzę, że OP mówił raczej o zmiennych/metodach chronionych (tj, niedostępnych poza klasą i jej pochodnymi). Twój przykład wylicza metody które byłyby publiczne więc potencjalnie używane przez kogokolwiek na zewnątrz. Wtedy oczywiste jest że niekoniecznie muszą one być używane wewnętrz klasy.

Cytuj
Jak chcesz mieć dobry design to najlepiej wcale nie dziedzicz. Same problemy z tym są. :)
Nieco mniej radykalnie: jeśli już dziedziczysz to nie po to żeby zbudować jakąś hierarchię klasyfikacji pojęć dla własnej przyjemności porządkowywania rzeczy, tylko żeby pomagało ci to w organizowaniu przepływu programu. Takim dość typowym przykładem który mogę podać (ryzykując aczkolwiek że mnie DODowcy zjedzą...) jest Object z metodami Update i Render po którym płasko dziedziczysz i implementujesz ww. metody żeby korzystać z polimorfizmu:
for (auto it = objects.begin(); it != objects.end(); ++it) {
    (*it)->Update(dt);
    (*it)->Render();
}

<shameless_plug>Incydentalnie, pisałem jakiś czas temu o tym dlaczego do dziedziczenia nie należy podchodzić jak do klasyfikacji gatunków czy czegoś równie złożonego.</shameless_plug>
« Ostatnia zmiana: Kwiecień 20, 2012, 11:01:00 wysłana przez Xion »

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Kwiecień 20, 2012, 18:01:44
Cytuj
Sądzę, że OP mówił raczej o zmiennych/metodach chronionych (tj, niedostępnych poza klasą i jej pochodnymi). Twój przykład wylicza metody które byłyby publiczne więc potencjalnie używane przez kogokolwiek na zewnątrz. Wtedy oczywiste jest że niekoniecznie muszą one być używane wewnętrz klasy.
Z metodami chronionymi jest tak samo. Klasa bazowa może dawać chronione metody pomocnicze, z których nie wszystkie pochodne korzystają (np. LoadPrivateImage który wczytuje obrazek kontrolki - jeżeli konkretna kontrolka nie ma obrazka, to tego nie wywołuje).