Nie rozumiem sensu programowania obiektowego

1

Błędnie podzielony na części składowe program proceduralny, łatwiej zmodyfikować niż błędnie podzielony na części program obiektowy.

Udowodnij.

3

Czym jest sens OOP, czyli jaki jest cel OOP? Proste ;)

Chodzi o zbudowanie rozwiązania problemu metodą Bottom-Up, gdzie dobrze zdefiniowane i nadające się do ponownego użycia rozwiązania (klasy) małych, dobrze zdefiniowanych problemów
składane są w większą całość. W podejściu strukturalnym, idzie to od drugiej strony, tzn. Top-Bottom, gdzie większy problem dzielimy na mniejsze, te na jeszcze mniejsze i rozwiązujemy te najmniejsze, które mogą, ale nie muszą nadawać się do ponownego wykorzystania.

Stąd też w OOP te wszystkie fabryki i interfejsy, bo jak bierzemy jakieś klocki (klasy) i chcemy zapewnić możliwość ich wymiany, to potrzebujemy wprowadzić "warstwę elastyczności", która to umożliwi. Im większą elastyczność chcemy, tym większa szansa, że skończymy z pierdylionem fabryk i interfejsów i ta elastyczność może złożonością przerosnąć rozwiązanie oryginalnego problemu (rozwiązania klasy "enterprajz").

3
Wibowit napisał(a):

Błędnie podzielony na części składowe program proceduralny, łatwiej zmodyfikować niż błędnie podzielony na części program obiektowy.

Udowodnij.

Nie wiem w jakiej formie dowody preferujesz, bo swoich własnych opinii dowodami nie podpierasz. Proszę, dla zilustrowania jak miałby taki dowód wyglądać, udowodnij - przykładowo - że przy pomocy OOP łatwiej utrzymywać duże projekty. Gdy zobaczę jak taki dowód może wyglądać, spróbuję na jego wzór zbudować własny - no chyba że Twój dowód będzie rzeczowy i bezbłędny i przekona mnie do zmiany zdania na temat OOP.

0

@Troll anty OOP:
pokaz kod ktory potwierdza Twoje zdanie. W taki sposob w jakis sposob udowodnisz

3

OOP tak naprawdę jest techniką, która ogranicza dowolność w pisaniu a daje niewiele. Właściwie jest tylko umową, że jeśli będziemy pisać w określony sposób, to wszystko będzie dobrze działać i w ogóle będzie tęczu. Tak samo jak z wcięciami: można nie robić wcięć wcale i też komuś to będzie pasować, poza tym jeden lubi spacje, drugi tabulacje.

2
yarel napisał(a):

Czym jest sens OOP, czyli jaki jest cel OOP? Proste ;)

Chodzi o zbudowanie rozwiązania problemu metodą Bottom-Up, gdzie dobrze zdefiniowane i nadające się do ponownego użycia rozwiązania (klasy) małych, dobrze zdefiniowanych problemów

Można, ale mało osób tak robi.

Projekty OOP właśnie bardzo czesto są pisane top-down, na zasadzie przeinżynierowania zaprojektowania całej apki za pomocą nawrzucania wzorców projektowych, klas, dziedziczenia, różnych innych syfów i dopiero potem się (niestety) myśli o konkretnych rzeczach, które aplikacja powinna robić pod kątem biznesowym.

Stąd też w OOP te wszystkie fabryki i interfejsy, bo jak bierzemy jakieś klocki (klasy) i chcemy zapewnić możliwość ich wymiany, to potrzebujemy wprowadzić "warstwę elastyczności",

Ale przecież właśnie te wszystkie wzorce projektowe sa często używane do nadania projektowi odgórnej struktury jeszcze przed napisaniem jakiegokolwiek kodu "biznesowego" - czyli mamy programowanie top-down.

Nie mówię, że jakaś postać programowania top-down i odgórnej struktury nie jest potrzebna, ale to co się wyrabia w wielu projektach OOP to jakaś masakra.

Oczywiście w OOP można programować bottom-up, i zgodziłbym się, że OOP to umożliwia w fajny sposób (enkapsulacja choćby czy podział na niezależne obiekty), tylko, że:

  1. to zależy od programistów czy będą pisali top-down czy bottom-up (gdzie żadna postać nie jest idealna - bo top-down zwykle kończy się na przeinżynierowaniu, a bottom-up może skończyć się na spaghetti kod, albo big ball of mud).
  2. to nie jest żadna przewaga OOP nad innymi paradygmatami (szczególnie biorąc pod uwagę programowanie funkcyjne, które jeszcze mocniej umożliwia takie pisanie, bo tam można wydzielać sobie poszczególne funkcje, które na dodatek nie mogą mieć skutków ubocznych, więc są jeszcze lepiej zenkapsulowane niż przeciętna klasa w OOP).
1
PerlMonk napisał(a):

OOP tak naprawdę jest techniką, która ogranicza dowolność w pisaniu a daje niewiele.

@PerlMonk: a w jaki konkretnie sposób OOP ogranicza wolność w pisaniu?

Właściwie jest tylko umową, że jeśli będziemy pisać w określony sposób, to wszystko będzie dobrze działać i w ogóle będzie tęczu.

A to z kolei, to kto stwierdził?

0
somekind napisał(a):
PerlMonk napisał(a):

OOP tak naprawdę jest techniką, która ogranicza dowolność w pisaniu a daje niewiele.

@PerlMonk: a w jaki konkretnie sposób OOP ogranicza wolność w pisaniu?

A no w taki, że jeśli chcesz nazwać kod obiektowym, to logika programu musi w jakiś sposób te obiekty reprezentować. Wyobraź sobie (czego ja oczekuję...), że w kodzie C++ nie użyjesz słów kluczowych struct, class i static do napisania kodu. Być może przyjdzie kolega i powie, że tam nie ma hermetyzacji i polimorfizmu.

Właściwie jest tylko umową, że jeśli będziemy pisać w określony sposób, to wszystko będzie dobrze działać i w ogóle będzie tęczu.

A to z kolei, to kto stwierdził?

Pewnie autor pojęcia OOP. Nikt nie mówi, że trzeba pisać określony kod w określonej strukturze, bo inaczej umrzemy.

2

Po pierwsze muszę przypomnieć najważniejsze, czyli definicję obiektu w OOPie. Obiekt to połączenie zachowania (metod/ funkcji) i stanu (pól). Stąd obiektem w sensie OOPa jest np taka struktura w C:

struct ToJestObiekt {
  int pole1;
  double pole2;
  int (*metoda1)(*ToJestObiekt);
  int (*metoda2)(*ToJestObiekt);
  int (*metoda3)(*ToJestObiekt);
}

Klasy (czy też np prototypy w JavaScripcie) są po to by zdeduplikować metody. Po co trzymać osobno wskaźniki do tej samej funkcji skoro można ten wskaźnik mieć raz w deklaracji klasy?

Dziedziczenie, enkapsulacja, itp dodatki są rozwinięciem OOPa, a nie jego fundamentem. Jeśli ktoś jest wkurzony na rozwinięcia OOPa to niech wskoczy np do Pythona - tam można spokojnie pisać obiektowo bez dziedziczenia i enkapsulacji, a nawet można usuwać albo dodawać metody do obiektów w locie (co jest oczywiście dalej OOPowe).

Troll anty OOP napisał(a):
Wibowit napisał(a):

Błędnie podzielony na części składowe program proceduralny, łatwiej zmodyfikować niż błędnie podzielony na części program obiektowy.

Udowodnij.

Nie wiem w jakiej formie dowody preferujesz, bo swoich własnych opinii dowodami nie podpierasz. Proszę, dla zilustrowania jak miałby taki dowód wyglądać, udowodnij - przykładowo - że przy pomocy OOP łatwiej utrzymywać duże projekty. Gdy zobaczę jak taki dowód może wyglądać, spróbuję na jego wzór zbudować własny - no chyba że Twój dowód będzie rzeczowy i bezbłędny i przekona mnie do zmiany zdania na temat OOP.

Przede wszystkim nie widzę potrzeby przekonywania cię do czegokolwiek. Co najwyżej warto by było przekonać cię, że OOP tak samo jak FP, kacze typowanie, statyczne typowanie, metaprogramowanie, itp itd to tylko narzędzia, które trzeba nauczyć się używać i wyrobić sobie intuicje co do tego kiedy które narzędzie jest przydatne.

Nie bardzo chce mi się szukać przykładów, bo temat jest o tym czym jest OOP a nie czy OOP jest dobry albo zły. Jednak natknąłem się na ciekawy przykład. Projekt jest duży i nazywa się Linux Kernel. Problem jest mały bo nazywa się lista wiązana. Oto jak koszmarnie wygląda implementacja listy wiązanej w C: https://kernelnewbies.org/FAQ/LinkedLists Przy tym czymś to LinkedList z Javy wygląda jak czyste złoto - można wyprodukować znacznie czytelniejszy kod do obsługi list, a brak potrzeby korzystania z makr kompilatora w Javie sprawia też, że komunikaty błędów kompilacji są zrozumiałe.

Osobną sprawą jest to, że alternatywą dla klas z OOPa mogą być np typeclassy z FP, ale te wcale nie muszą być prostsze. Hierarchie zależności między typeclassami też mogą być mocno przeinżynierowane. W ogólności każdy elastyczny sposób abstrakcji może być nadużyty bo można go nawarstwiać i komplikować w nieskończoność. Czy w takim razie powinniśmy schodzić na niski poziom? Przykład list wiązanych z Linuksa pokazuje, że to wcale nie upraszcza sprawy. Implementacja wygodnej w użyciu w OOPie rzeczy jest toporna w języku C.

1
Wibowit napisał(a):

Nie bardzo chce mi się szukać przykładów, bo temat jest o tym czym jest OOP a nie czy OOP jest dobry albo zły. Jednak natknąłem się na ciekawy przykład. Projekt jest duży i nazywa się Linux Kernel. Problem jest mały bo nazywa się lista wiązana. Oto jak koszmarnie wygląda implementacja listy wiązanej w C: https://kernelnewbies.org/FAQ/LinkedLists Przy tym czymś to LinkedList z Javy wygląda jak czyste złoto - można wyprodukować znacznie czytelniejszy kod do obsługi list, a brak potrzeby korzystania z makr kompilatora w Javie sprawia też, że komunikaty błędów kompilacji są zrozumiałe.

Taka implementacja listy z C posiada dodatkowe właściwości, których standardowe kontenery z C++ nie mają (z Javy zapewne też nie, ale wolę się wypowiedzieć o C++ bo jest mi bardziej znane): element może być na kilku listach jednocześnie, albo może być jednocześnie w tablicy i na liście. I wędrując po jednej liście, po dojściu do interesującego elementu, można zacząć od tego elementu i iść po drugiej. Nie wiem co prawda, czy ta właściwość jest w kernelu wykorzystywana, ale wydaje mi się to prawdopodobne.

Ja używam analogicznej implementacji listy w userspace i zdarza mi się, że mam elementy w tablicy a jednocześnie na liście. I na przykład dostaję się do elementu przez indeks w tablicy, robię jakąś modyfikację, po czym przesuwam go w inne miejsce na liście (np. liście-harmonogramie, kiedy najpóźniej trzeba wykonać jakąś operację na tym elemencie). W innym momencie korzystam wyłącznie z listy-harmonogramu zamiast tablicy.

Te operacje na makrach, w przypadku akurat listy, może ładne nie są, ale da się dość szybko przyzwyczaić i wcale nie tak znowu łatwo o popełnienie błędu, przed którym zabezpieczałby interfejs listy jaki jest w C++. Ja w każdym razie nie mam żadnych złych doświadczeń z taką implementacją listy.

0

W Javce można po prostu wrzucić referencję do tego samego obiektu do dowolnej ilości list i tyle. W C też można, aczkolwiek w C będziesz miał ten problem, że nie będzie wiadomo kto ma odpalić free przy wywalaniu węzłów z list.

Taka implementacja listy z C posiada dodatkowe właściwości, których standardowe kontenery z C++ nie mają (z Javy zapewne też nie, ale wolę się wypowiedzieć o C++ bo jest mi bardziej znane): element może być na kilku listach jednocześnie, albo może być jednocześnie w tablicy i na liście. I wędrując po jednej liście, po dojściu do interesującego elementu, można zacząć od tego elementu i iść po drugiej. Nie wiem co prawda, czy ta właściwość jest w kernelu wykorzystywana, ale wydaje mi się to prawdopodobne.

Z punktu widzenia typowego użytkownika listy jest to przeinżynierowanie, a obsługa takiej listy jest nieprzyjemna.

5
PerlMonk napisał(a):

A no w taki, że jeśli chcesz nazwać kod obiektowym, to logika programu musi w jakiś sposób te obiekty reprezentować.

Owszem, muszą być obiekty. Inaczej nie byłoby sensu nazywać tego obiektowym, to chyba logiczne?

Wyobraź sobie (czego ja oczekuję...), że w kodzie C++ nie użyjesz słów kluczowych struct, class i static do napisania kodu. Być może przyjdzie kolega i powie, że tam nie ma hermetyzacji i polimorfizmu.

No być może przyjdzie i powie, być może będzie miał rację - tylko jaki to ma związek z moim pytaniem i Twoją tezą? Gdzie konkretnie znajduje się "ograniczenie dowolności"?
Albo idąc dalej - czym jest ta "dowolność" i co właściwie daje?

Właściwie jest tylko umową, że jeśli będziemy pisać w określony sposób, to wszystko będzie dobrze działać i w ogóle będzie tęczu.

A to z kolei, to kto stwierdził?

Pewnie autor pojęcia OOP. Nikt nie mówi, że trzeba pisać określony kod w określonej strukturze, bo inaczej umrzemy.

W takim razie poproszę o konkretny cytat konkretnej osoby.

nullpt4 napisał(a):

problemów które występują tylko w OOP :P

Niby masz rację, tylko ona nie prowadzi do wniosków, które chciałbyś ironicznie wyciągnąć.
Jeśli masz swój dom, to też musisz rozwiązywać problemy (przegląd kominów, czyszczenie rynien, koszenie trawników), których nie masz mieszkając pod mostem. Czy lepiej wybrać most?

W programowaniu proceduralnym trudno o wzorce projektowe, bo nie ma niczego, co można by projektować ani składać ze sobą na różne sposoby. Stosowanie obiektów rodzi potrzebę ich organizacji, a wzorce projektowe służą właśnie temu celowi. A problemy nie wynikają z OOP per se (bo istnienie paradygmatu nie tworzy problemów), tylko z zadań, które są pochodną wymagań.

. jasne, tylko o ten kod ludzie dbali, był nawet osobny zespół który analizował np. zależności między klasami i starał się jakoś to uprościć posługując się min wzorcami projektowymi.

To tak jakby jedna ekipa budowlana stawiała ściany krzywo, a druga potem popychała je patykami próbując wypionować pod wpływem lektury książki o poziomicach. Nie umiem sobie wyobrazić jakim cudem miałoby to zadziałać.

Ale zawsze jest szansa że pisali go sami debile włącznie ze mną :)

Debilizm oznacza lekkie upośledzenie umysłowe. Dość niefortunne określenie sytuacji, w której jedni ludzie klepią cokolwiek, a potem drudzy próbują to przerobić na kod. To jest po prostu zwykły nonsens.

To tak nie działa, że można sobie najpierw coś zwymiotować na klawiaturę, a potem to przerobić zgodnie z wzorcami i SOLID. Nie da się napisać idiomatycznie w OOP niczego sensownego (realizującego cel domenowy, nie hello worlda z kotkami i pieskami dziedziczącymi po sobie) nie stosując SOLID ani wzorców, bo albo wyjdzie z tego programowanie proceduralne (być może nawet na globalnym stanie) albo copy-paste. Więc albo pisze się od razu dobrze (i ma się OOP), albo ma się procedury w języku z klasami upstrzone dodatkowo losowymi elementami OOP, które jednak wcale związku z tym paradygmatem nie mają.

chodzi mi o wyrażanie abstrakcji korzystając z darów OOP, które w pewnym momencie prowadzi do ślepych uliczek i zagmatwania kodu.

Jeśli napisze się w sposób zagmatwany (np. stosując wszędzie interfejsy i fabryki, bo gdzieś się wyczytało, że na tym polega OOP), to będzie zagmatwane. Ale to nie jest efekt prawidłowego użycia OOP w miejscu jego zasadnego użycia.

0
somekind napisał(a):

Jeśli masz swój dom, to też musisz rozwiązywać problemy (przegląd kominów, czyszczenie rynien, koszenie trawników), których nie masz mieszkając pod mostem. Czy lepiej wybrać most?

Oczywiście, że nie.
OOP jest domem, a wszystko inne mostem? :P

W programowaniu proceduralnym trudno o wzorce projektowe, bo nie ma niczego, co można by projektować ani składać ze sobą na różne sposoby.

bo nie ma niczego,co można by projektować Nie ma tego całego cyrku który rodzi problemy, Tak samo jest w FP, ale to nie przeszkadza w projektowaniu i składaniu, tylko pomaga. Po porażce współczesnego OOP zacząłem się interesować innymi paradygmatami i wykorzystywać w praktyce. Nie wiem jak będzie na dłuższą metę, ale jak na razie widzę kolosalną różnicę, na plus oczywiście. Podoba mi się też OOP w wydaniu twórcy tego paradygmatu.

Stosowanie obiektów rodzi potrzebę ich organizacji, a wzorce projektowe służą właśnie temu celowi. A problemy nie wynikają z OOP per se (bo istnienie paradygmatu nie tworzy problemów), tylko z zadań, które są pochodną wymagań.

W OOP są problemy np. z organizacją obiektów, a wzorce projektowe te problemy starają się rozwiązać. Nie ma ich w innych paradygmatach, tzn te wzorce przydają się do problemów które występują tylko w OOP, więc OOP jest pośrednią/bezpośrednią przyczyną tych problemów.

To tak jakby jedna ekipa budowlana stawiała ściany krzywo, a druga potem popychała je patykami próbując wypionować pod wpływem lektury książki o poziomicach. Nie umiem sobie wyobrazić jakim cudem miałoby to zadziałać.

Nie pracowali nad tym studenci po tutorialu o OOP, tylko bardzo doświadczeni ludzie. Kod może być dobrze napisany, ale przychodzą nowe wymagania, przez które dochodzą nowe zależności pomiędzy obiektami, których wcześniej nie musiało być, dlatego byli ludzie którzy nad myśleli (rozmawiając też z zespołami ofc) jak to wszystko przeorganizować żeby nie było spagetti.

której jedni ludzie klepią cokolwiek, a potem drudzy próbują to przerobić na kod. To jest po prostu zwykły nonsens.

ehhh, klepanie czegokolwiek ... . OOP jest genialne i bez skazy tylko ludzie to ch***

To tak nie działa, że można sobie najpierw coś zwymiotować na klawiaturę, a potem to przerobić zgodnie z wzorcami i SOLID.

Jasne, że nie. Kto mówił o zwymiotowaniu na klawiaturę i przerabianiu na SOID'a? chodzi mi o wyrażanie abstrakcji korzystając z darów OOP, które w pewnym momencie prowadzi do ślepych uliczek i zagmatwania kodu. Z pomocą przychodzi SOLID, wzorce projektowe i cała reszta, zaczyna to lepiej wyglądać, Nie mówiłem tutaj o projekcie, tylko bardziej ogólnie, gdzie końcówkę zadania ale po jakimś czasie developmentu problem powracał, a dobre praktyki i GoF przestawały pomagać doświadczyłem w projekcie w którym pracowałem. Tzn było OOP, którego nie wymyślali ludzie którzy dopiero co przeczytali tutorial o wzorcach. Projekt wyglądał ok, ale przychodziły nowe wymagania, które dodawały np zależności między obiektami których(zależności) wcześniej nie było, i mądrzy goście się zastanawiali jak to ogarnąć, ale po kilku udanych próbach, implementacja kolejnych funkcjonalności które wymagały dodania interakcji między obiektami, gdzie ich wcześniej nie było, stała się bardzo trudna. Oczywiście projekt ma ponad 10 lat.

(np. stosując wszędzie interfejsy i fabryki, bo gdzieś się wyczytało, że na tym polega OOP), to będzie zagmatwane. Ale to nie jest efekt prawidłowego użycia OOP w miejscu jego zasadnego użycia.

Oczywiście, że tak, ale kto tak robi? OOP ma problemy, na które odpowiedzią są np wzorce projektowe, ale niestety one też mają swoje granice.

2
nullpt4 napisał(a):

Stosowanie obiektów rodzi potrzebę ich organizacji, a wzorce projektowe służą właśnie temu celowi. A problemy nie wynikają z OOP per se (bo istnienie paradygmatu nie tworzy problemów), tylko z zadań, które są pochodną wymagań.

W OOP są problemy np. z organizacją obiektów, a wzorce projektowe te problemy starają się rozwiązać. Nie ma ich w innych paradygmatach, tzn te wzorce przydają się do problemów które występują tylko w OOP, więc OOP jest pośrednią/bezpośrednią przyczyną tych problemów.

WTF?! Chętnie usłyszę, jakie są problemy w OOP które nie występują w innych paradygmatach, Wzorce rozwiązuję problemy, ale jako zagadnienia, a nie coś co sprawia trudność/kłopoty i to w taki sposób, by było to reużywalne i zrozumiałe przez innych. No sorry, ale niby w programowaniu funkcyjnym, czy proceduralnym nie ma zagadnień tworzenie struktur kodu implementujących wzory Obserwatora, Strategii, Fasady, Pyłku?

Programowanie obiektowe nie jest żadnym złem, de facto większość ludzi na świecie stara się programować obiektowo i większość kodu jest napisanych obiektowo (mimo, że w językach nie wspierających tego). Nawet Linux oraz Windows jest napisany w paradygmacie OOP.

Negowanie OOP to tak jakby odmrozić sobie uszy na złość mamie... To, że część osób nie rozumie niektórych mechanizmów OOP, a inne są szkodliwe(przez kiepskie zrozumienie ich większości np. dziedziczenie wielobazowe), nie oznacza, że cały OOP jest do bani...

Ostatnio mam wrażenie, że forum zalewa fala osób, względnie młodych co czegoś tam się nauczyli i wydaje im sie, że sporo wiedzą i posiedli mityczną prawdę...

6
nullpt4 napisał(a):

OOP jest domem, a wszystko inne mostem? :P

Nie zrozumiałeś analogii. Nie chodziło o to, co jest czym, ale o to, że coś co daje większe możliwości wymaga też większych umiejętności obsługi.

Ale jesli chcesz stosować takie metafory, to w takim razie programowanie proceduralne jest takim szałasem/lepianką. W odpowiednim warunkach i skali zadziała, ale jeśli chcesz, aby na jednym hektarze mieszkało 5 tys ludzi z dostępem do wody i ciepła, to raczej się nie uda.

Nie ma tego całego cyrku który rodzi problemy,

Ale tak konkretnie, to czym jest ten cyrk, który rodzi problemy? Bo cały czas czytam, jak to wszystko jest złe, ale żaden konkret jeszcze nie padł.

Nie pracowali nad tym studenci po tutorialu o OOP, tylko bardzo doświadczeni ludzie.

No chyba jednak nie bardzo, o czym świadczy dalsza część historii. Chyba, że za doświadczenie uznajesz niesłusznie staż pacy. Tego można mieć nawet tysiąc lat i nadal nie rozumieć, o co chodzi. Spotkalem dziesiątki takich osób.

Kod może być dobrze napisany, ale przychodzą nowe wymagania, przez które dochodzą nowe zależności pomiędzy obiektami, których wcześniej nie musiało być, dlatego byli ludzie którzy nad myśleli (rozmawiając też z zespołami ofc) jak to wszystko przeorganizować żeby nie było spagetti.

Nie, nie może być dobrze napisany w takiej sytuacji. Jeśli na skutek nowych wymagań zmienia się istniejący kod, to znaczy, że łamie się SOLID, konkretnie OCP oraz najprawdopodobniej SRP. Gdyby ze zrozumieniem użyto wzorców projektowych, to problemy by nie występowały.
A jeśli ktoś nie przewidział, że wymagania się zmienią i nie zaprojektował struktury kodu tak, aby umożliwiała wprowadzanie zmian, to znaczy, że nie był za bardzo doświadczony.

Generalnie większość "problemów z kodem OOP" jakie widziałem w życiu (a widziałem bardzo dużo słabego kodu), wynikały z tego, że:

  1. Ktoś z całego OOP zrozumiał, że chodzi o dziedziczące po sobie klasy, więc tworzył bardzo skomplikowane hierarchie próbując umieścić w nich cały kod. Hierarchie te oczywiście szybko stawały się niemożliwe do utrzymania, a wprowadzanie zmian wymagało dodawania kolejnych drabinek if i is.
  2. Ktoś z całego OOP zrozumiał, że obiekt to połączenie danych i operacji na nich, tworzył więc wielkie obiekty, które wczytywały dane z dysku, zapisywały do bazy, wysyłały po sieci, itd.
  3. Ktoś programował proceduralnie z klasami - mając hierarchę danych do przetworzenia i procedury na nich operujące, które np. sprawdzają typ danych wejściowych, i w zależności od niego wykonują inną część kodu.
  4. Ktoś naczytał się o dobrych praktykach i próbował je wdrażać wszędzie, za to bezmyślnie i bezsensownie. Stąd się bierze jeden interfejs do każdej klasy, do tego jeszcze fabryka, wszystkie klasy rejestrowane w kontenerze IoC, brak static i mutowalnych struktur danych, które rzekomo są zakazane, itd., itp.
  5. Połączenie wszystkich powyższych.

Wszystko to wynika z niezrozumienia OOP, i braku stosowania SOLID oraz wzroców oraz tkwienia w proceduralnym myśleniu. Jedynie punkt czwarty wynika z jakiegoś sekciarstwa i nadmiernego stosowania wzorców, bo "takie są dobre praktyki", co również jest efektem ignorancji. Ale wszystko to jest efektem działań ludzi, a nie istnienia narzędzia.

Nie mówiłem tutaj o projekcie, tylko bardziej ogólnie, gdzie końcówkę zadania ale po jakimś czasie developmentu problem powracał, a dobre praktyki i GoF przestawały pomagać doświadczyłem w projekcie w którym pracowałem. Tzn było OOP, którego nie wymyślali ludzie którzy dopiero co przeczytali tutorial o wzorcach. Projekt wyglądał ok, ale przychodziły nowe wymagania, które dodawały np zależności między obiektami których nie było, i mądrzy goście się zastanawiali jak to ogarnąć, ale po kilku udanych próbach, implementacja kolejnych funkcjonalności które wymagały dodania interakcji między obiektami, gdzie ich wcześniej nie było, stała się bardzo trudna. Oczywiście projekt ma ponad 10 lat.

Zapewne przejście na programowanie proceduralne, wszystkie problemy rozwiąże. :)

1

OOP dzieli kod na logiczne bloki, którymi łatwo zarządzać, zwłaszcza w dużych projektach.
Dodatkowo mamy wzorce projektowe, które ułatwiają nam komunikacje z innymi programistami, powiesz nazwę wzorca projektowego i każdy ma już obraz o czym jest mowa. Większość problemów można sprowadzić do szeregu wzorców i tym samym stworzyć dobrą architekturę wzorców.
Problem w tym że wielu programistów nie wie do końca jak należy stosować wzorce, co prowadzi do tworzenia takich rzeczy:

data class Rectangle(val a :Int, val b) {
 ...
}

data class Square(a :Int) : Rectangle(a, a) {
 ...
}

albo

class BaseLogger {
    fun log(message : String) {
        ...
    }
}

class JakasKlasa : BaseLogger {
   ...
}

4
somekind napisał(a):

Kod może być dobrze napisany, ale przychodzą nowe wymagania, przez które dochodzą nowe zależności pomiędzy obiektami, których wcześniej nie musiało być, dlatego byli ludzie którzy nad myśleli (rozmawiając też z zespołami ofc) jak to wszystko przeorganizować żeby nie było spagetti.

Nie, nie może być dobrze napisany w takiej sytuacji. Jeśli na skutek nowych wymagań zmienia się istniejący kod, to znaczy, że łamie się SOLID, konkretnie OCP oraz najprawdopodobniej SRP. Gdyby ze zrozumieniem użyto wzorców projektowych, to problemy by nie występowały.
A jeśli ktoś nie przewidział, że wymagania się zmienią i nie zaprojektował struktury kodu tak, aby umożliwiała wprowadzanie zmian, to znaczy, że nie był za bardzo doświadczony.

Nawet ludzie z ogromnym doświadczeniem niektórych zmian nie przewidzą. A z tych potencjalnych potrzeb, które przewidywali jako możliwe, tylko niewielka część rzeczywiście stanie się potrzebna, więc uwzględnianie ich wszystkich na samym początku stworzy tylko paraliż, przez który nigdy nie powstanie nawet prototyp. A nawet jakby było tak dużo czasu, że można by uwzględnić wszystkie potencjalne zmiany, które tworzącym przychodzą do głowy na początkowym etapie, to i tak objawi się nieunikniony problem: taka jest ludzka natura, że zazwyczaj nie wpada się od razu za pierwszym razem na rozwiązanie idealne. Zwykle potrzebny jest etap dochodzenia do rozwiązania metodą prób i błędów. Porównujesz dyskutanta i jego współpracowników z jakimś teoretycznym bytem nie popełniającym błędów i robiącym już za pierwszym razem idealnie - nie dziwne, że w takim porównaniu żyjący ludzie wypadają słabo.

Generalnie większość "problemów z kodem OOP" jakie widziałem w życiu (a widziałem bardzo dużo słabego kodu), wynikały z tego, że:
[...]

Streszczając to, co zastąpiłem przez "[...]": metoda dobra, tylko źle używana.
Jednak łatwość/trudność użycia narzędzia dobrze, oraz czy bardziej intuicyjne jest jego użycie dobre czy złe, też są ważnymi argumentami w ocenie jakości narzędzia. Z tego co piszesz, ludzie bardzo często używają OOP niepoprawnie. Czy więc rzeczywiście jest dobre, że ciągle brną w używanie czegoś, czego użycie im nie wychodzi?

Może odpowiedzią na rozpoczynające ten wątek pytanie powinno być:
"Jest bardzo prawdopodobne, że użyjesz OOP źle, nawet jeśli poświęcisz dużo czasu na jego naukę, więc lepiej zostań przy tym, co stosujesz obecnie." ?

4
Troll anty OOP napisał(a):

Może odpowiedzią na rozpoczynające ten wątek pytanie powinno być:
"Jest bardzo prawdopodobne, że użyjesz OOP źle, nawet jeśli poświęcisz dużo czasu na jego naukę, więc lepiej zostań przy tym, co stosujesz obecnie." ?

Można też rozwinąć tę tezę: jest bardzo prawdopodobne, że napiszesz dużo szajsowatego kodu, więc odpuść sobie kodowanie w ogóle.

Ja bym poszedł w drugą stronę: poznaj OOP, poznaj FP, poznaj różne języki, podejścia i narzędzia, by rozszerzyć horyzonty. Później będzie łatwiej ci ocenić co się w twoim przypadku sprawdza, a co nie. Ja tak zrobiłem i nie żałuję.

0

W sumie jak by się głębiej zastanowić nad tematem, to największym plus OOP na innymi paradygmatami programowania to jest polimorfizm. Ogólnie jak dla mnie upychanie na siłę wszystkiego w klasach jest bezsensem. Już kiedyś poruszałem gdzieś ten temat, ale wtedy zwolennicy OOP mnie przekopali :D

0

Polimorfizm możesz mieć i bez OOP. Dla przykładu w Ruście masz typeclassy pod nazwą traitów. Rustowe traity mogą mieć czasami hierarchie bardzo podobne do hierarchii obiektowych. Trait objects w Ruście natomiast pozwalają w pewnych przypadkach na OOPowy polimorfizm (OOPowego dziedziczenia jednak nadal nie ma) - w zasadzie można powiedzieć, że trait objects są OOPowym elementem Rusta.

Typeclasses są też w językach funkcyjnych, np Haskellu (od niego pochodzą). Mają swoje hierarchie i związane z tym problemy natury projektowej. Które metody mają mieć domyślne implementacje, a które mają być czysto abstrakcyjne? Jakie mają być zależności między typeclassami? Szereg problemów, które mają swoje analogie w OOPie.

Poza tym polimorfizm i tak wszyscy używają. Jak ktoś pisze w Pythonie i nie używa wprost dziedziczenia to i tak tworzy niejawne hierarchie klas, gdy np jedna zmienna w metodzie przyjmuje różne typy w zależności od tego co ją wywołuje. Makro w C czy szablon w C++ może operować na różnych typach danych - ważne jest tylko, by kod wypluty przez preprocesor się kompilował.

Ponadto OOPowy polimorfizm można zaimplementować i w czystym C. Wcześniej podałem prosty przykład z wrzucaniem wskaźników do funkcji od struktur z danymi. Bardziej skomplikowany pomysł na zaimplementowanie OOPa w czystym C to https://en.wikipedia.org/wiki/GObject - GObject jest podstawą GTK+ i GNOME. Jak widać w GNOME nawet się nie czają i nie udają, że nie używają OOPa - nazwali swoją abstrakcję wprost obiektem.

0
Wibowit napisał(a):

Polimorfizm możesz mieć i bez OOP.

Całkiem możliwe, a nawet pewnie tak jest jak piszesz. W sumie moja odpowiedź była w kontekście świata w jakim ja się kręcę, dlatego jak ktoś ma szersze horyzonty to może być niepoprawna lub wręcz rażąca w oczy :)

2

Z perspektywy wannabe programmer OOP wydaje się dla mnie trudne (niezrozumiałe), bo dalej nie widzę eleganckiego rozwiązania następujących problemów:

  1. Walidacja danych wejściowych. Przesyłasz do metody stringa, który może być częścią stanu obiektu? Upewnij się, że nie jest nullOrEmpty! Przesyłasz inta? Upewnij się, że nie jest ujemny! W efekcie dochodzi to tego, że połowa kodu encji to jakieś asercje preconditions, które są duplikacją walidacji z warstwy wyżej. Tu przykład, jeśli ktoś nie wierzy.

  2. Komunikacja między obiektami. Można rzucić wyjątek, jeśli coś pójdzie nie tak (patrz przykład wyżej), ale potem odbiorca musi robić jakieś brzydkie try-catch. Można zwrócić Either/Result, ale wymaga to więcej pracy, a poza tym nie widziałem w żadnym publicznym repo, by ktoś tak robił. Można też zrobić jakieś dziwne rzeczy w stylu if (object.canIDoSth()) object.doSth(), ale to wygląda na wyciek logiki poza encję.

  3. Modelowanie rzeczywistości. Powiedzmy, że mamy klasę Order. Zgodnie z OOP powinniśmy dać jej jakąś metodę changeStatus, jeśli chcemy zmienić stan zamówienia. Tylko czy w rzeczywistości zamówienie samo zmienia swój status? No nie, zmienia ten status obiekt Employee. Ponieważ pracownik jest odpowiedzialny też za wiele innych rzeczy, to jeśli chcemy zamodelować poprawnie rzeczywistość, to powinniśmy mieć jakąś boską klasę Employee z dostępem do źródła danych operującą na strukturach danych (np. Order). Brzmi znajomo?

Wszystkie powyższe przykłady dotyczą sytuacji, gdy używamy OOP do modelowania domeny.

4
nobody01 napisał(a):
  1. Modelowanie rzeczywistości. Powiedzmy, że mamy klasę Order. Zgodnie z OOP powinniśmy dać jej jakąś metodę changeStatus, jeśli chcemy zmienić stan zamówienia. Tylko czy w rzeczywistości zamówienie samo zmienia swój status? No nie, zmienia ten status obiekt Employee. Ponieważ pracownik jest odpowiedzialny też za wiele innych rzeczy, to jeśli chcemy zamodelować poprawnie rzeczywistość, to powinniśmy mieć jakąś boską klasę Employee z dostępem do źródła danych operującą na strukturach danych (np. Order). Brzmi znajomo?

Miałem podobne problemy, gdy uczyłem się podstaw. Wg mnie myślenie o dosłownym modelowaniu rzeczywistości to pułapka. Kod obiektowy ma łączyć dane z metodami operujących na tych danych. To jest podstawa. Jeżeli kod odnosi się w pewien sposób do rzeczywistości, to jest łatwiejszy do zrozumienia. Ale dosłowne modelowanie, to syzyfowa praca nie warta czasu.
Równie dobrze mógłbyś myśleć, że jeżeli zamówienie ma metodę zmiany statusu, to nie jest to inteligentny obiekt, który sam sobie zmienia status, lecz reaguje na żądanie zmiany statusu, ukrywając mało istotne szczegóły implementacji. Kod związany z operowaniem na bebechach zamówienia nie jest w obiekcie pracownika, a właśnie zamówienia, bo jest to sensowny sposób połączenia kodu i danych, tj. enkapsulacji danych i wystawienia odpowiedniego interfejsu do operowania na tych danych. Być może nawet ten pracownik nie jest istotna abstrakcją i nie powinnien znajdować się w systemie.
W końcu chcesz stworzyć coś, co rozwiązuje jakiś konkretny problem, a nie dosłownie modelować świat.

2

Kod obiektowy ma łączyć dane z metodami operujących na tych danych. To jest podstawa.

Dokładnie tak. Pisałem o tym wielokrotnie w tym wątku, ale zawsze można to powtórzyć jeszcze raz. Stworzenie klasy, która jest pojemnikiem na dane to nie jest OOP. To jest właśnie programowanie strukturalne.

Wydaje mi sie ze dosłowne modelowanie to cecha DDD, a nie samego OOP. - tdudzik dziś, 16:34

Czytam książkę Implementing DDD Vaughna Vernona i tam jest napisane coś wprost przeciwnego:
http://www.informit.com/articles/article.aspx?p=1944876&seqNum=4

  1. Agile, Iterative, Continuous Modeling
    The word “design” can evoke negative thoughts in the minds of business management. However, DDD is not a heavyweight, high ceremony design and development process. DDD is not about drawing diagrams. It is about carefully refining the mental model of domain experts into a useful model for the business. It is not about creating a real-world model, as in trying to mimic reality.

Na razie jestem na początku książki, więc nie mam wyrobionych intuicji dotyczących DDD. Naiwnych i błędnych intuicji typu "przepchałem funkcję do funkcji, więc programuję funkcyjnie", "stworzyłem klasę, więc programuję obiektowo", "wygenerowałem event, więc mam event sourcing" i tak dalej chcę oczywiście uniknąć.

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