Wątek przeniesiony 2019-07-29 15:33 z przez cerrato.

Nie rozumiem sensu programowania obiektowego

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.

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