class Fish extends Animal?…

2011-09-22 22:24

Pisałem już wcześniej o tym, że ostatnimi czasy programowanie obiektowe nie ma zbyt dobrej prasy i swego rodzaju modą stało się jego krytykowanie. Nie jest to oczywiście trend zupełnie pozbawiony podstaw. Z jednej bowiem strony dla pewnych problemów lepsze wydają się inne paradygmaty: np. dla modelu żądanie-odpowiedź (typowego dla aplikacji webowych) najwyraźniej całkiem dobrze sprawdza się programowanie funkcyjne. Z kolei zastosowania wymagające dużej wydajności (np. programowanie grafiki 3D czasu rzeczywistego) mogą istotnie nie tolerować narzutu związanego z polimorfizmem funkcji wirtualnych – albo przynajmniej tak się wydaje tym, którzy się nimi zajmują.

Sądzę jednak, że spory udział ma tu też pewien powszechny (niestety) zwyczaj krytykowania rzeczy, których nie do końca rozumiemy. Szerokie kręgi zatacza bowiem pewien specyficzny sposób opacznego interpretowania idei OOP-u. Jego źródła upatruję w wielu (może nawet większości) kursach, książkach, tutorialach, artykułach i wszelkiego rodzaju materiałach instruktażowych dla początkujących adeptów programowania. Sam dodałem swój niechlubny wkład do tego stanu rzeczy, do czego się tu otwarcie przyznaję. No cóż, nikt mnie ostrzegł – ani wtedy (czyli jakieś 6 lat temu), ani nawet teraz.
A ostrzeżenie jest absolutnie niezbędne. Także dlatego, że co najmniej raz słyszałem, jak owo mylne pojęcie na temat OOP-u stało się poważnym argumentem w rzeczowej poza tym dyskusji na temat jego zalet i (głównie) wad. Nawet ważniejszym powodem jest jednak to, iż niewłaściwa interpretacja założeń programowania obiektowego może prowadzić do źle zaprojektowanych systemów, które trudno się potem konserwuje i rozbudowuje.

O jakiego więc rodzaju konfuzji tu mówimy?

Spójrzmy na poniższy diagram – jest on niezwykle typowy. Istnieje spora szansa, że dowolny tekst wprowadzający do OOP-u będzie zawierał podobny obrazek – choć zapewne nie będzie on używał notacji tak formalnej jak UML (Przykładu nie trzeba zresztą daleko szukać).

Przy użyciu podobnych analogii wyjaśnia się idee dziedziczenia, polimorfizmu, a czasem samego pojęcia obiektu. Wygląda to zapewne całkiem sensownie, bo pozwala odnieść koncepcje programistyczne do codziennego doświadczenia. Rozumiemy, jak wywodzenie się ryby od zwierzęcia pozwala na dziedziczenie wspólnych – za przeproszeniem – funkcjonalności z jednoczesną możliwością ich modyfikacji i uzupełnienia. Jest to więc zgrabna metafora.

Ale jest to tylko metafora, która tak naprawdę jest raczej kiepska. Nadużyciem jest czynienie z niej naczelnej idei programowania obiektowego, które jakoby ma na celu modelowanie “realnych” obiektów i przekładanie rzeczowników ze specyfikacji na klasy w kodzie. To nie jest nadinterpretacja – to po prostu nieprawda. Lecz naturalnie jest to jednocześnie bardzo wygodne nadużycie, bo pozwala bezkarnie zżymać na przeróżne -ory i -ery (managery, iteratory, visitory, listenery itp.) jako “nieodnoszące się do rzeczywistości”, a przecież występujące powszechnie w każdym większym projekcie obiektowym. Najwyraźniej więc OOP sam sobie przeczy, przynajmniej na linii teoria-praktyka… czyż nie?

Spieszę z odpowiedzią: oczywiście nie. Obiekt to nie jest kawałek realnego świata żywcem przeniesiony do kodu. Dziedziczenie nie jest sposobem na zaspokojenie geekowej potrzeby klasyfikowania elementów przestrzeni rzeczy. Polimorfizm zaś nie jest metodą spoglądania na te same pojęcia z różnych stron. Nie, żadna z tych koncepcji nie ma aż takich pretensji. Są one znacznie mniej ambitne, a przez to o wiele bardziej użyteczne.

Obiekt to pewne dane wraz z odnoszącymi się do nich operacjami. Dziedziczenie to sposób na ponowne użycie kodu i uniknięcie powtórzeń. Polimorfizm pozwala natomiast na ukrycie detali implementacyjnych, podobnie jak robią to moduły, funkcje czy nawet zmienne.

Tylko tyle, względnie niewiele więcej. Nikt nie zmusza nas, żeby każda klasa i każdy obiekt był intuicyjnym, sugestywnym pojęciem, łatwo odnoszącym się do rzeczywistych rzeczy i ich kategorii. Może to być po prostu zbiór funkcji wraz ze wspólnymi dla nich danymi – wydzielony np. dlatego, że zawierająca je wcześniej klasa stała się już zbyt duża (a my mamy pecha i piszemy w języku, który wszystko każe pakować w klasy).
Nie musi za tym stać żadna wyrafinowana filozofia. Dobrze natomiast, żeby stało dążenie do uczynienia kodu jak najbardziej czytelnym i łatwym do późniejszej modyfikacji. W miejscach, gdzie te cele stają się sprzeczne (albo niezgodne z innymi ważnymi celami, np. wydajnością), obiektowy “puryzm” zawsze musi ustąpić.

A tak naprawdę to nie powinien mieć w ogóle prawa głosu.

Tags: , , ,
Author: Xion, posted under Programming, Thoughts »


5 comments for post “class Fish extends Animal?…”.
  1. MDW:
    September 23rd, 2011 o 7:44

    I ja się z tym zgadzam w całej rozciągłości.

  2. Kauach:
    September 23rd, 2011 o 13:34

    Amen =D

  3. Kokos:
    September 23rd, 2011 o 23:47

    @Xion
    Poleciłem wpis na ‘fejsie’ (link na moim nicku, zobacz). Logo YouTube nie pasuje za bardzo :P

  4. Kos:
    November 3rd, 2011 o 15:29

    Xion, doceniam Twoją intencję, ale NIEEE wkręcaj ludziom, że dziedziczenie jest sposobem na ponowne wykorzystanie kodu… Jest, ale zwykle dość marnym :D (implementowanie mixinów dziedziczeniem?).

    Zawsze w pierwszej kolejności trzeba rozważać zwykłą kompozycję, bo niepotrzebne piętrzenie hierarchii dziedziczenia to jeden z tych błędów, przez które ludzie potem psioczą na OOP.

    Dziedziczenie w pierwszej kolejności służy do *wprowadzania abstrakcji*, ni do czegokolwiek innego. Kropka!

  5. Xion:
    November 3rd, 2011 o 18:59

    @Kos: Oczywiście że dziedziczenie jest sposobem na ponowne wykorzystanie kodu – bo między innymi temu służy każde wprowadzenie abstrakcji :) A że często nie jest najlepszym, to już zupełnie inna sprawa ;P

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.