Pułapki programowania obiektowego                               Wersja 2, 29 marca 2011       Adam Sawicki – www.asawicki....
Agenda Kim jestem „Fanatyzm obiektowy”  • Projektowanie obiektowe  • Data-Oriented Design „Mania wrapowania”  • Wrapper...
Adam Sawicki adam@asawicki.info www.asawicki.info @Reg__ Programista  • Politechnika Częstochowska – Informatyka  • Pr...
Programowanie obiektowe Teoria: program składa się z klas  • Łączą dane (pola) i kod (metody)  • Modelują byty z dziedzin...
Fanatyzm obiektowy5
Projektowanie obiektowe OOP można rozważad na różnych płaszczyznach:  • Ideologiczna – zasady programowania obiektowego i...
PrzykładGra w kółko i krzyżyk – podejście obiektowe1. Znajdujemy klasy identyfikując rzeczowniki 7
Przykład2. Projektujemy powiązania między klasami 8
Przykład3. Implementujemy klasy Co umieścid w tych klasach, żeby nie były puste??? W której klasie umieścid potrzebne da...
Przykład3. Implementujemy klasy Co umieścid w tych klasach, żeby nie były puste??? W której klasie umieścid potrzebne da...
Data-Oriented Design Alternatywne podejście:  Data-Oriented Design (DOD)                                            enum ...
DOD – przykład Następnie pomyśl, co po kolei kod powinien robid z tymi  danymi  • Wykonanie ruchu przez gracza  • Sprawdz...
DOD – przykład Następnie pomyśl, co po kolei kod powinien robid z tymi  danymi  • Wykonanie ruchu przez gracza  • Sprawdz...
Projektowanie obiektowe – wnioski Podejście obiektowe nie jest idealne Można nawet stwierdzid, że nie spełniło swoich  z...
Mania wrapowania15
Mania wrapowania Wielu programistów czuje potrzebę, żeby naukę czy  wykorzystanie w swoim programie jakiejś biblioteki  z...
Przykład 1: FMODBiblioteka dźwiękowa FMOD – wczytanie dźwięku WAVFMOD::Sound *Sound;FmodSystem->createSound(  WavFileName,...
Przykład 1: FMODJimmy pisze własną klasę dźwięku, która zamyka FMOD::Soundclass CSound {private:  FMOD::Sound *Sound;publi...
Przykład 1: FMOD Jimmy potrzebuje możliwości zapętlenia dźwięku  • Musi dodad parametr Loop  • Musi zamieniad parametr ze...
Przykład 1: FMOD Jimmy odkrywa odtwarzanie w dwie strony i też chce to  umożliwid  • Musi zdefiniowad własny enum LOOP_MO...
Przykład 1: FMOD Wreszcie duża częśd możliwości FMOD jest  przepisana do własnego interfejsu Trzeba zrobid do niego włas...
Przykład 2: DirectX Zrobię wrapper na DirectX, to potem  będę mógł wymienić renderer na  OpenGL bez modyfikowania kodu gr...
Przykład 2: DirectX Zrobię wrapper na DirectX, to potem  będę mógł wymienić renderer na  OpenGL bez modyfikowania kodu gr...
Przykład 2: DirectX Jakie jest prawdopodobieostwo, że  • P1 – skooczysz kod swojej gry/silnika  • P2 – będziesz potrzebow...
Przykład 3: WinAPI i MFC Ktoś w Microsoft:  WinAPI jest niefajne, bo jest strukturalne. Napiszmy  obiektową otoczkę.// Wi...
Wrapper – wnioski Jaka wobec tego jest alternatywa?  • W porę zastanowid się, czy nie wystarczy używad w swoim    program...
WarstwyThe object-oriented version of"Spaghetti code" is, of course,"Lasagna code". (Too manylayers.) – Roberto WaltmanAll...
Generalizacja28
Generalizacja – błędne koło              1.Chcemy, żeby każdy pisany kod był jak                najbardziej ogólny i uniwe...
Generalizacja – błędne koło              2. Unikamy wprowadzania zmian w                 kodzie,                 bo każda,...
Generalizacja – błędne koło              3. Każda zmiana w kodzie wymaga                 bardzo dużo pracy,               ...
Generalizacja – błędne koło Rozwiązanie  • Pisz w sposób prosty, bezpośrednio to co ma byd napisane i    nie więcej.  • N...
Dziedziczenie33
Dziedziczenie Teoria  • Modeluje relację „jest rodzajem”  • Umożliwia abstrakcję i polimorfizm  • Dzieli obszerny kod na ...
Dziedziczenie Wady  • Kod jednej funkcjonalności jest rozproszony między    wiele klas i plików  • Tworzy ścisłe powiązan...
Dziedziczenie Przykład: okrąg i elipsa  • Matematyk mówi: okrąg jest rodzajem elipsy,    która ma dwa promienie równe!  •...
DziedziczenieRozwiązanie: Myśl o danych Rozważ inne możliwości  •   enum Type  •   Kompozycja (agregacja)  •   Podejście...
Enkapsulacja38
Enkapsulacja Teoria  • Udostępnia interfejs, ukrywa szczegóły implementacji  • Pozwala zmienid implementację bez zmian w ...
Enkapsulacja Problem  • Czym się różni getter + setter od uczynienia pola publicznym?  • Czy naprawdę wierzysz, że możesz...
EnkapsulacjaRozwiązanie: Nie bój się używad struktur z publicznymi polami tam,  gdzie chodzi o paczkę danych Rozważ inne...
Wzorce projektowe:              Singleton42
Singleton Teoria  • Tylko jedna instancja klasy  • Dostępna globalnie  • Inicjalizowana przy pierwszym użyciu            ...
SingletonRozwiązanie: Jawnie tworzyd i niszczyd obiekty globalne w określonym  miejscu programu                        g_...
Podsumowanie45
OOP kontra DOD:(:)     46
OOP kontra DOD          class BaseObject {                                        ??          private:                    ...
Podsumowanie DOD służy raczej do optymalizacji wydajności  • Kod bardziej przyjazny dla pamięci cache  • Kod łatwiej się ...
Bibliografia Fanatyzm obiektowy, Adam Sawicki  • http://www.asawicki.info/Download/Misc/Fanatyzm_obiektowy.html Materiał...
Pytania?50
Upcoming SlideShare
Loading in...5
×

Pułapki programowania obiektowego

880
-1

Published on

"Pitfalls of Object-Oriented Programming" [Polish] Prezentacja do wykładu przygotowanego na Koło Naukowe Twórców Gier "Polygon" na Politechnice Warszawskiej, a w drugiej wersji wygłoszonego także na AGH w Krakowie.

Opracowana na podstawie tekstu Fanatyzm obiektowy, przedstawia krytyczne podejście do projektowania i programowania obiektowego (m.in. pisania wrapperów na używane biblioteki, nadmiernej generalizacji, nadużywania dziedziczenia, enkapsulacji, wzorców projektowych) oraz jego negatywny wpływ na prostotę i czytelność kodu. Jako alternatywę prezentuje podejście DOD (Data-Oriented Design).

Published 2011-03-30

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
880
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
4
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Pułapki programowania obiektowego

  1. 1. Pułapki programowania obiektowego Wersja 2, 29 marca 2011 Adam Sawicki – www.asawicki.info
  2. 2. Agenda Kim jestem „Fanatyzm obiektowy” • Projektowanie obiektowe • Data-Oriented Design „Mania wrapowania” • Wrapper – na 3 przykładach Generalizacja Dziedziczenie Enkapsulacja Wzorce projektowe • Singleton Podsumowanie • DOD raz jeszcze Pytania 2
  3. 3. Adam Sawicki adam@asawicki.info www.asawicki.info @Reg__ Programista • Politechnika Częstochowska – Informatyka • Praktyki w siedzibie Microsoft w Redmond, USA • AquaFish 2 (gra wydana przez PLAY Publishing) • Metropolis Software (They – shooter na PC i X360) • Cyfrowy Polsat S.A. 3
  4. 4. Programowanie obiektowe Teoria: program składa się z klas • Łączą dane (pola) i kod (metody) • Modelują byty z dziedziny problemu • Są od siebie niezależne • Nadają się do ponownego użycia Założenia • Enkapsulacja (hermetyzacja) – udostępnienie interfejsu, ukrycie implementacji • Polimorfizm i dziedziczenie – używanie obiektu abstrakcyjnego bez rozróżniania konkretnych typów 4
  5. 5. Fanatyzm obiektowy5
  6. 6. Projektowanie obiektowe OOP można rozważad na różnych płaszczyznach: • Ideologiczna – zasady programowania obiektowego i abstrakcyjne znaczenie jego założeo, • Techniczna – jak się używa programowania obiektowego w danym języku i jak ono działa, • Praktyczna – jak używad go w programach dla swojego pożytku, a nie tylko dla zasady. Tak jak ze wszystkim, nadmierna radykalnośd i ślepe poparcie bez myślenia rodzi fanatyzm, a to jest złe. 6
  7. 7. PrzykładGra w kółko i krzyżyk – podejście obiektowe1. Znajdujemy klasy identyfikując rzeczowniki 7
  8. 8. Przykład2. Projektujemy powiązania między klasami 8
  9. 9. Przykład3. Implementujemy klasy Co umieścid w tych klasach, żeby nie były puste??? W której klasie umieścid potrzebne dane i kod???Analysis Paralysis – poszukiwanie idealnego rozwiązania 9
  10. 10. Przykład3. Implementujemy klasy Co umieścid w tych klasach, żeby nie były puste??? W której klasie umieścid potrzebne dane i kod???Analysis Paralysis – poszukiwanie idealnego rozwiązania 10
  11. 11. Data-Oriented Design Alternatywne podejście: Data-Oriented Design (DOD) enum POLE { • Myśl o strukturze danych w pamięci POLE_PUSTE, POLE_KOLKO, POLE_KRZYZYK, Pytanie: Jakie dane powinna }; POLE Plansza[3][3]; przechowywad gra? int CzyjRuch; • Tablica 3 x 3 pól • Każde pole jest w jednym ze stanów: puste, kółko, krzyżyk • Czyj jest teraz ruch: gracza 1 lub 2 11
  12. 12. DOD – przykład Następnie pomyśl, co po kolei kod powinien robid z tymi danymi • Wykonanie ruchu przez gracza • Sprawdzenie, czy ruch jest prawidłowy • Wstawienie symbolu do tablicy • Sprawdzenie, czy gracz wygrał • Rozpoczęcie tury przeciwnego gracza 12
  13. 13. DOD – przykład Następnie pomyśl, co po kolei kod powinien robid z tymi danymi • Wykonanie ruchu przez gracza • Sprawdzenie, czy ruch jest prawidłowy • Wstawienie symbolu do tablicy • Sprawdzenie, czy gracz wygrał • Rozpoczęcie tury przeciwnego gracza 13
  14. 14. Projektowanie obiektowe – wnioski Podejście obiektowe nie jest idealne Można nawet stwierdzid, że nie spełniło swoich założeo • Modelowanie rzeczywistych obiektów? Co modeluje manager, helper, listener, obeserver, locker i inne -ery? • Źle użyte działa przeciwko wydajności, jak też prostocie i czytelności kodu Warto je stosowad, ale z rozwagą • Znaj i doceniaj inne możliwości • Nie szukaj gotowych „klocków” uciekając od myślenia 14
  15. 15. Mania wrapowania15
  16. 16. Mania wrapowania Wielu programistów czuje potrzebę, żeby naukę czy wykorzystanie w swoim programie jakiejś biblioteki zacząd od napisania własnej otoczki (wrappera) zamykającego jej interfejs. • Robią to niemal odruchowo i bez zastanowienia, czy to potrzebne. Jakie argumenty za tym stoją? 16
  17. 17. Przykład 1: FMODBiblioteka dźwiękowa FMOD – wczytanie dźwięku WAVFMOD::Sound *Sound;FmodSystem->createSound( WavFileName, FMOD_LOOP_OFF | FMOD_2D | MOD_SOFTWARE, 0, &Sound); Jimmy jest przerażony, chce uprościd interfejs • Te parametry są niepotrzebne • Chcę mied mniej parametrów • Nie chcę sięgad do dokumentacji FMOD – wolę własny interfejs 17
  18. 18. Przykład 1: FMODJimmy pisze własną klasę dźwięku, która zamyka FMOD::Soundclass CSound {private: FMOD::Sound *Sound;public: void Load(const char *WavFileName);};void CSound::Load(const char *WavFileName) { FmodSystem->createSound( WavFileName, FMOD_LOOP_OFF | FMOD_2D | FMOD_SOFTWARE, 0, &Sound);} 18
  19. 19. Przykład 1: FMOD Jimmy potrzebuje możliwości zapętlenia dźwięku • Musi dodad parametr Loop • Musi zamieniad parametr ze swojej postaci do postaci FMODvoid CSound::Load(const char *WavFileName, bool Loop) { FmodSystem->createSound( WavFileName, (Loop ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF) | FMOD_2D | FMOD_SOFTWARE, 0, &Sound);} 19
  20. 20. Przykład 1: FMOD Jimmy odkrywa odtwarzanie w dwie strony i też chce to umożliwid • Musi zdefiniowad własny enum LOOP_MODE • Musi zamieniad swój enum na ten z FMODenum LOOP_MODE { LOOP_MODE_NONE, LOOP_MODE_NORMAL, LOOP_MODE_BIDI };void CSound::Load(const char *WavFileName, LOOP_MODE LoopMode) { unsigned LoopFlag = 0; switch (LoopMode) { case LOOP_MODE_NONE: LoopFlag = FMOD_LOOP_OFF; break; case LOOP_MODE_NORMAL: LoopFlag = FMOD_LOOP_NORMAL; break; case LOOP_MODE_BIDI: LoopFlag = FMOD_LOOP_BIDI; break; } FmodSystem->createSound(WavFileName, LoopFlag | FMOD_2D | FMOD_SOFTWARE, 0, &Sound);} 20
  21. 21. Przykład 1: FMOD Wreszcie duża częśd możliwości FMOD jest przepisana do własnego interfejsu Trzeba zrobid do niego własną dokumentację Zamiast flag FMOD trzeba pamiętad własne Jaka to różnica? Warto było?? 21
  22. 22. Przykład 2: DirectX Zrobię wrapper na DirectX, to potem będę mógł wymienić renderer na OpenGL bez modyfikowania kodu gry. 22
  23. 23. Przykład 2: DirectX Zrobię wrapper na DirectX, to potem będę mógł wymienić renderer na OpenGL bez modyfikowania kodu gry. 23
  24. 24. Przykład 2: DirectX Jakie jest prawdopodobieostwo, że • P1 – skooczysz kod swojej gry/silnika • P2 – będziesz potrzebował mied drugą implementację renderera • P3 – uda się zaimplementowad renderer w OpenGL bez większych zmian w jego interfejsie P1 ∙ P2 ∙ P3 ≈ 0 Znasz już dobrze zarówno DirectX, jak i OpenGL? • Jeśli nie → P3 = 0 24
  25. 25. Przykład 3: WinAPI i MFC Ktoś w Microsoft: WinAPI jest niefajne, bo jest strukturalne. Napiszmy obiektową otoczkę.// WinAPI // MFCHDC hdc = GetWindowDC(hwnd); CDC *dc = wnd->GetDC();MoveToEx(hdc, 0, 0, NULL); dc->MoveTo(0, 0);LineTo(hdc, 100, 100); dc->LineTo(100, 100);ReleaseDC(hdc); wnd->ReleaseDC(dc); Czy to zrobiło aż tak dużą różnicę? Warto było??? Efekt? • MFC to tylko cienka otoczka na WinAPI. Nikt go nie lubi. 25
  26. 26. Wrapper – wnioski Jaka wobec tego jest alternatywa? • W porę zastanowid się, czy nie wystarczy używad w swoim programie interfejsu danej biblioteki bezpośrednio. Warto napisad wrapper, kiedy zapewnia przynajmniej jedno z poniższych: • Znacząco upraszcza interfejs • Dodaje naprawdę dużo nowej funkcjonalności • Wprowadza dużo wyższy poziom abstrakcji • Faktycznie posiada kilka różnych implementacji 26
  27. 27. WarstwyThe object-oriented version of"Spaghetti code" is, of course,"Lasagna code". (Too manylayers.) – Roberto WaltmanAll problems in computerscience can be solved byanother level of indirection…Except for the problem of toomany layers of indirection. –David Wheeler 27
  28. 28. Generalizacja28
  29. 29. Generalizacja – błędne koło 1.Chcemy, żeby każdy pisany kod był jak najbardziej ogólny i uniwersalny, aby nie trzeba było wprowadzad w 3 1 nim zmian, kiedy zmienią się :( wymagania. 229
  30. 30. Generalizacja – błędne koło 2. Unikamy wprowadzania zmian w kodzie, bo każda, nawet miała zmiana, 3 1 wymaga bardzo dużo pracy. :( 230
  31. 31. Generalizacja – błędne koło 3. Każda zmiana w kodzie wymaga bardzo dużo pracy, ponieważ wszystko piszemy jak 3 1 najbardziej ogólnie i uniwersalnie. :( 231
  32. 32. Generalizacja – błędne koło Rozwiązanie • Pisz w sposób prosty, bezpośrednio to co ma byd napisane i nie więcej. • Nie wyprzedzaj przyszłości. Kiedy pojawi się taka potrzeba, wtedy przerobisz kod na bardziej ogólny. 32
  33. 33. Dziedziczenie33
  34. 34. Dziedziczenie Teoria • Modeluje relację „jest rodzajem” • Umożliwia abstrakcję i polimorfizm • Dzieli obszerny kod na fragmenty mające częśd wspólną 34
  35. 35. Dziedziczenie Wady • Kod jednej funkcjonalności jest rozproszony między wiele klas i plików • Tworzy ścisłe powiązania między klasami • Zmiana w jednym miejscu powoduje nieoczekiwane efekty w innych miejscach • Często trudno zaprojektowad dobry układ klas, ich pól, metod i zależności między nimi 35
  36. 36. Dziedziczenie Przykład: okrąg i elipsa • Matematyk mówi: okrąg jest rodzajem elipsy, która ma dwa promienie równe! • Programista obiektowy mówi: elipsa jest rodzajem okręgu, który dodaje drugi promieo! ?? 36
  37. 37. DziedziczenieRozwiązanie: Myśl o danych Rozważ inne możliwości • enum Type • Kompozycja (agregacja) • Podejście sterowane danymi (data-driven) • Architektura komponentowa Stosuj dziedziczenie jak najrzadziej, nie jak najczęściej Patrz na dziedziczenie jak na mechanizm językowy, nie jak na ideę do modelowania każdej relacji „jest rodzajem” 37
  38. 38. Enkapsulacja38
  39. 39. Enkapsulacja Teoria • Udostępnia interfejs, ukrywa szczegóły implementacji • Pozwala zmienid implementację bez zmian w interfejsie i w innych częściach kodu class Foo { public: int GetValue() { return Value; } void SetValue(int v) { Value = v; } private: int Value; }; 39
  40. 40. Enkapsulacja Problem • Czym się różni getter + setter od uczynienia pola publicznym? • Czy naprawdę wierzysz, że możesz zmienid implementację tego pola (zmienid jego typ, wyliczad za każdym razem, pobierad z innego obiektu) bez zmian w interfejsie i w innych częściach kodu?class Foo {public: int GetValue() { return (int)Value; } void SetValue(int v) { Value = (float)v; }private: float Value;}; 40
  41. 41. EnkapsulacjaRozwiązanie: Nie bój się używad struktur z publicznymi polami tam, gdzie chodzi o paczkę danych Rozważ inne metody dostępu do danych, np. parametry przekazywane podczas inicjalizacji, a potem dostępne tylko do odczytu • Descriptor, immutability struct FooDesc { int Value; }; class Foo { public: Foo(const FooDesc &params); void GetParams(FooDesc &out_params); }; 41
  42. 42. Wzorce projektowe: Singleton42
  43. 43. Singleton Teoria • Tylko jedna instancja klasy • Dostępna globalnie • Inicjalizowana przy pierwszym użyciu SoundSystem::GetInstance().PlaySound("Boom!"); Wady • Brak jawnej kontroli nad kolejnością inicjalizacji i finalizacji • Narzut na każde wywołanie • Problem z bezpieczeostwem wątkowym Czy na pewno system dźwiękowy powinien się inicjalizowad w momencie, kiedy chcemy odegrad pierwszy dźwięk? 43
  44. 44. SingletonRozwiązanie: Jawnie tworzyd i niszczyd obiekty globalne w określonym miejscu programu g_SoundSystem = new SoundSystem();Every time you make a singleton, God kills a startup.Two if you think youve made it thread-safe. – popularnasentencja z Twittera 44
  45. 45. Podsumowanie45
  46. 46. OOP kontra DOD:(:) 46
  47. 47. OOP kontra DOD class BaseObject { ?? private: Render float3 position; Render:( public: Render Render virtual void Render() = 0; }; struct RenderBatch { float3 position; Frustum Sort / Culling Group Render Mesh *mesh; Material *material;:) }; 47
  48. 48. Podsumowanie DOD służy raczej do optymalizacji wydajności • Kod bardziej przyjazny dla pamięci cache • Kod łatwiej się zrównolegla Jednak wielu odrzuca „przedwczesną optymalizację” nadinterpretując znany cytat Donalda Knutha Dlatego tutaj pokazałem, jak krytyczne spojrzenie na OOP może pomóc także w uproszczeniu kodu. 48
  49. 49. Bibliografia Fanatyzm obiektowy, Adam Sawicki • http://www.asawicki.info/Download/Misc/Fanatyzm_obiektowy.html Materiały o Data-Oriented Design • http://www.asawicki.info/news_1422.html (lista linków) 49
  50. 50. Pytania?50
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×