Wprowadzenie

Czym jest dziedziczenie?

Dziedziczenie umożliwia tworzenie nowych klas, które ponownie wykorzystują, rozszerzają i modyfikują zachowanie zdefiniowane w innych klasach. Klasa, której elementy są dziedziczone, nazywana jest klasą bazową, a klasa, która dziedziczy te elementy, nazywana jest klasą pochodną. Klasa pochodna może mieć tylko jedną bezpośrednią klasę bazową.

Konstrukcja ta wywodzi sie z generalizacji: jej założeniem było usuwanie z kodu domniemanych redundancji na wzór pojęciowego związku generalizacji. Kluczowy problem polega na tym, że takie podejście czyni z każdej aplikacji monolit o bardzo złożonej strukturze kodu źródłowego. Problem w tym, że formalnie dziedziczenie nigdy nie było cechą (istotą) obiektowego paradygmatu programowania, a jednak mimo to producenci “obiektowo-zorientowanych” języków programowania podkreślali tę cechę swoich narzędzi, podchwyciły to także uczelnie i w programach nauczania i podręczników, pod pojęciem paradygmatu obiektowego, promowano takie cechy jak “dziedziczenie i łączenie danych i funkcji w obiekty”, które de facto obie są antywzorcami projektowania.

W efekcie powstały ogromne ilości kodu bardzo trudnego w utrzymaniu i rozwoju co zaczęło powodować narastająca dyskusję na temat tego problemu. Artykuł zainspirowany referatem umieszczonym na końcu tego tekstu.

Krótka historia problemu

1965 – Simula, pierwszy obiektowy język programowania.

1966 – Alan Kay publikuje ideę Obiektowego Paradygmatu Programowania, nie jest to dziedziczenie i łączenie funkcji i danych w obiekty, Paradygmat Obiektowy to hermetyczne (zamknięte) obiekty i wymiana komunikatów między nimi, kluczową cechą obiektów jest hermetyzacja i polimorfizm (odpowiedź na żądanie zależy od tego do jakiego obiektu zostało ono wysłane, a nie od tego jak ono brzmi).

1972 – powstaje obiektowo zorientowany język Smalltalk, nie wspiera on dziedziczenia.

1985 – powstaje C++, wersja C pozwalającą na pisanie w duchu OOP+dziedziczenie.

1995 – “Kompozycja zamiast dziedziczenia: Faworyzuj kompozycję obiektów zamiast dziedziczenia klas”. [Gamma, Erich, ed. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional Computing Series. Addison-Wesley, 1995.]

1998 – narasta problem z systemami ERP i FinTech, bo kończy sie era mainframe a zaczyna PC, a aplikacje zbudowane są często na modelach relacyjnych danych (model relacyjny powstał w 1972), pełna, bezstratna migracja z baz relacyjnych do innych formatów w zasadzie jest niemożliwa, więc powstał pomysł by te tabele relacyjne odwzorować w kodzie (mapowanie ORM, dziedziczenie, kompozycje): powstała JavaEE (Oracle) i słynny “anemiczny model dziedziny”, który już po niecałych 10 latach uznano za antywzorzec projektowy.

1998 – Alan Kay, pomysłodawca “obiektowości”, dostrzega problem i przypomina: “Paradygmat obiektowy to nie dziedziczenie i łączenie funkcji i danych w obiekty! To hermetyzacja obiektów i wymiana komunikatów między obiektami!”

1998Profesor Ousterhout ( Stanford University) pisze: “Innym problemem związanym z językami obiektowymi jest ich nacisk na dziedziczenie. Dziedziczenie implementacji, w którym jedna klasa pożycza kod napisany dla innej klasy, jest złym pomysłem, który utrudnia zarządzanie oprogramowaniem i jego ponowne wykorzystanie. Wiąże ono ze sobą implementacje klas w taki sposób, że żadna z nich nie może być zrozumiana bez drugiej: podklasa nie może być zrozumiana bez wiedzy o tym, jak odziedziczone metody są zaimplementowane w jej nadklasie, a nadklasa nie może być zrozumiana bez wiedzy o tym, jak jej metody są dziedziczone w podklasach. W złożonej hierarchii klas, żadna pojedyncza klasa nie może być zrozumiana bez zrozumienia wszystkich innych klas w hierarchii. Co gorsza, klasa nie może być oddzielona od swojej hierarchii w celu ponownego użycia. Dziedziczenie wielokrotne jeszcze bardziej pogarsza te problemy. Dziedziczenie implementacji powoduje to samo przeplatanie i kruchość, które zaobserwowano w przypadku nadużywania instrukcji goto. W rezultacie systemy zorientowane obiektowo często cierpią z powodu złożoności i braku możliwości ponownego wykorzystania.” [Ousterhout, J.K. ‘Scripting: Higher Level Programming for the 21st Century’. Computer 31, no. 3 (1998): 23–30. https://doi.org/10.1109/2.660187].

2002 – Microsoft konkurując z Oracle (Java) wypuszcza oficjalną wersję .NET Framework z językiem C# (zbudowany na wzór C++), podobnie jak Java wykorzystuje dziedziczenie i ORM.

2003 – A. Cockburn (sygnatariusz Agile Manifesto) wskazuje, ze wzorce projektowe znane z C++/JavaEE są dobre dla środowiska (elementy systemu) ale szkodliwe dla aplikacji biznesowych, publikuje “Architekturę Heksagonalną” (separacja kodu logiki biznesowej aplikacji i kodu środowiska aplikacji)

2007 – jedna z bardziej znanych publikacji opisująca szkodliwość odwzorowywania modeli pojęciowych bezpośrednio w kodzie z pomocą dziedziczenia.

2014 – Fowler (sygnatariusz Agile Manifesto) oficjalnie krytykuje dziedziczenie i anemiczny model dziedziny (klasy odwzorowujące pojęcia i ich związki) jako antywzorzec projektowy.

2015 – OMG usuwa związek dziedziczenia z UML (zostaje jedynie generalizacja).

2015 – W odpowiedzi pojawia się jeden z bardziej znanych referatów: Bjarne Stroustrup – Object Oriented Programming without Inheritance – ECOOP 2015.



2018 – R.C. Martin wraz Simonem Brownem (Arch. C4) publikują “Clean Architecture”, opisują swoje pozytywne doświadczenia z podziałem na komponenty, separacją aplikacji od jej środowiska i używaniem UML na etapie projektowania poprzedzającego kodowanie. Całkowicie ignorują dziedziczenie jako element architektury (patrz także ciekawy, jeden z wielu podobnych, artykuł z tego samego roku na ten temat: Inheritance is Indeed Evil).

2020 – dość głośny artykuł w Medium zatytułowany “Inheritance is Indeed Evil” [Kock, Martin. ‘Inheritance Is Indeed Evil’. Medium, 19 January 2020. https://medium.com/@cookie80/inheritance-is-indeed-evil-ee759b87ad4d.]

2020 – referat Anjana Vakil, która opisuje jak wyciąga projekty z kłopotów: jest to całkowita rezygnacja z języków C++ i JavaEE i rezygnacja z dziedziczenia w kodzie:

Ten wykład to historyczna i filozoficzna podróż w głąb serca ciemności, czyli programowania obiektowego (OOP). Dołącz do mnie, gdy będę wstrząśnięty odkryciem, że obiekty i klasy nie są najważniejszymi koncepcjami OOP: są nimi wiadomości i późne wiązanie. Spróbujemy zajrzeć do głów Alana Kaya i innych twórców OOP, gdy tworzyli języki takie jak Smalltalk, i odkryjemy, że te “stare” pomysły wydają się dziś uderzająco aktualne. Nasze szczęki mogą opaść, gdy zdamy sobie sprawę, że OOP i programowanie funkcyjne nie różnią się tak bardzo, jak nam się wydawało, a pierwszy język OO nie powstał w latach 60-tych czy 70-tych, ale dużo, dużo wcześniej… Co czeka nas na końcu tej podróży? W najgorszym przypadku przejdziemy krótki kryzys wiary we wszystko, co kiedykolwiek myśleliśmy, że wiemy o programowaniu. (W najlepszym przypadku zmienimy sposób, w jaki postrzegamy ten niemal powszechny, ale często niezrozumiany paradygmat i odejdziemy z nowymi spostrzeżeniami na temat tego, jak projektujemy i rozumiemy nasz kod.

2021 – referat Dave’a Farley’a “Object Oriented Programming vs Functional Programming”, w którym wskazuje, że paradygmat obiektowy to nie dziedziczenie:

Programowanie obiektowe było dominującym podejściem przez ostatnie kilka dekad, ale języki programowania funkcjonalnego zyskują coraz większą popularność i wpływy. Czy zatem programowanie obiektowe jest przestarzałe? Czy programowanie funkcyjne to moda, która po prostu mija się z celem?

Wielu programistów funkcyjnych uważa, że programowanie obiektowe jest złe. Wielu programistów OO uważa, że programowanie funkcyjne nie jest skalowalne, jaka jest prawda?

W tym odcinku Dave Farley analizuje kwestię inżynierii oprogramowania dotyczącą programowania obiektowego i funkcjonalnego. Skąd wzięły się te idee, co oznaczają i dlaczego mają znaczenie. Ponadto, czy istnieją inne pomysły, które mogą pojawić się w przyszłości?

2025 – czerwiec 17, głośny referat Casey”a Muratori: “The Big OOPs: Anatomy of a Thirty-five-year Mistake” – BSC 2025 (Better Software Conference):

Moja krótka historia

1985 – moje pierwsze programy w Fortranie (tu mnie nauczono rysowania przed kodowaniem, pomaga do dzisiaj).

1989 – Basic, Pascal, C, C++, pierwsze przygody z kodowaniem i nauczaniem programowania studentów. Uznałem, że dziedziczenie to zmora, od tej pory nie korzystałem z tej konstrukcji.

1991 – początek przygody z system UNIX, projektowanie dedykowanych podsystemów dla dużych firm i urzędów, głównie Perl.

1995 – po przygodach z ORM i dziedziczeniem w Oracle (nazywali to obiektowe bazy danych) odpuściłem sobie całkowicie SQL i relacyjne modele danych, wiele moich aplikacji zaczęło powstawać na systemie plików w UNIX (kilkaset tysięcy rekordów bywało szybsze niż tabele Informix), później przeszedłem na SGML, potem na XML, motory SQL zacząłem wykorzystywać tylko jak pojedyncze tabele, tak jak późniejsze bazy key-value i bazy dokumentowe (NoSQL).

1998 – pierwsze profesjonalne przygody z UML w projekcie dla banku (system scoringowy dla Kredyt Bank SA)

2004 – rozpoczęcie pracy w roli samodzielnego kontraktowego analityka-projektanta aplikacji (nadal).

2005 – dostałem zaproszenie do prowadzenia wykładów i laboratoriów dla studentów Akademii Morskiej (obecnie Uniwersytet Morski) w Gdyni, później Akademia WIT w Warszawie przy Polskiej Akademii Nauk.

Jarosław Żeliński

Jarosław Żeliński: Po ukończeniu WAT w 1989 roku pracownik naukowy katedry Transmisji Danych i Utajniania. Od roku 1991 roku, po rozpoczęciu pracy w roli analityka i projektanta systemów przetwarzania informacji, nieprzerwanie realizuje kolejne projekty dla urzędów, firm i organizacji. Od 1998 roku prowadzi także samodzielne studia i prace badawcze z obszaru analizy systemowej i modelowania systemów (modele jako przedmiot badań: ORCID), publikując je nieprzerwanie także na tym blogu. Od 2005 roku, jako wykładowca akademicki wizytujący (nieetatowy), prowadzi wykłady i laboratoria (ontologie i modelowanie systemów informacyjnych, aktualnie w Wyższej Szkole Informatyki Stosowanej i Zarządzania pod auspicjami Polskiej Akademii Nauk w Warszawie.). Od 2020 roku na stałe mieszka w Szkocji (Zjednoczone Królestwo), nadal realizuje projekty dla firm i organizacji także w Polsce. Każdy z Państwa może zostać Mecenasem Bloga: Chcę zostać Mecenasem Autora Bloga

Ten post ma 5 komentarzy

  1. Marek

    Same sprzeczności, nawet nie chciało mi się czytać całości tylko przebiegłem po łebkach. Obiekt to nie funkcje i dane tylko hermetyzacja i komunikaty a komunikaty to niby czym są? Można pisać bez dziedziczenia ale po co aby sobie utrudniać pracę i rozpychać program.

    1. Jarosław Żeliński

      “Można pisać bez dziedziczenia ale po co aby sobie utrudniać pracę i rozpychać program.”

      Dziedziczenie to właśnie utrudnianie, wystarczy porównać liczbę linii kodu i jego złożoność. Rezygnacja z dziedziczenia czasami nawet o rząd wielkości obniża koszt utrzymania i rozwoju aplikacji, wystarczy porównywać oferty różnych deweloperów: koszt pracy i oferowane technologie.

    2. Jarosław Żeliński

      “a komunikaty to niby czym są?”. Komunikaty są komunikatami: np. string JSON/XML 😉 (nie mylić z wywołaniem operacji).

  2. Dominik

    W takim wypadku można ubiegać się o odszkodowanie na uczelni za wpajane zło ? 🙂

Skomentuj Marek Anuluj pisanie odpowiedzi

Ta strona używa Akismet do redukcji spamu. Dowiedz się, w jaki sposób przetwarzane są dane Twoich komentarzy.