Programowanie obiektowe - co o nim sądzicie?

0

Co do pytania o definicję, to ja programowanie obiektowe zdefiniowałbym jako paradygmat gdzie z daną strukturą (która staje się klasą) jest formalnie związany zestaw prodecur (stających się metodami) zajmujących się nimi. Enkapsulacja jest dobra, i pewnie dobrze jak jest na poziomie języka, ale nie spisywałbym na straty języków obiektowych jej nie mających - wtedy musi o to zadbać programista, np. dodając polom i metodom "prywatnym" jakiś przedrostek w nazwie. Ważną cechą OOP, różniącą klasę od modułu jest mnogość obiektów, tzn. można zaprogramować powiedzmy taką linked list tak, jakby była jedna, a następnie używać wielu. Dziedziczenie bez polimorfizmu ma jak dla mnie mały sens. Jeśli chodzi o wydajność, to problemem jest właśnie polimorfizm, ale jeżeli go nie stosujemy, to można obyć się bez znaczących strat wydajnościowych - np. klasy w C++. Właściwie to wystarczy po prostu przekazywać jako dodatkowy parametr metodom wskaźnik na this. Java ma tu inne podejście - każda metoda wirtualna - ale w Javie priorytetem nie jest wydajność. Ogólnie doceniam OOP głównie za przejrzystą organizację większych programów. Nie jest jednak też tak, że uważam wszystkie nieobiektowe techniki za złe lub przestarzałe, nie wszystko musi być koniecznie obiektowe. Czasami część programu można zrobić obiektowo, a resztę klasycznie, i to nawet w Javie się da - za pomocą składowych statycznych.

0
LukeJL napisał(a):
hzmzp napisał(a):

piszesz na jakiś małe arm gdzie flash masz parę kb a ramu jeszcze mniej to pisanie w oop jest po prostu debilne.
A jakie dokładnie elementy w danej implementacji OOP powodują, że jest ona niewydajna?

Czyli tak jeśli masz Obiekt, który enkapsuluje wewnątrz inny obiekt potem inny lub jakieś inheritance.

To masz potem takie coś Obiekt->innyObiekt->inny->etc;
To jak ktoś w pętli za każdym razem by przechodził ten obiekt od góry do dołu to na 1000 wywołań 1000 razy przejdzie, ale jak będzie tylko do ostatniego elementu się odwoływał to wydajność nic się nie pogorszy, będzie tak samo jakby nie istniał cały ten ciąg powiązań.
Chodź i tak kompilator jest na tyle inteligentny, że przy optymalizacji wykryje takie zachowanie i nie będzie za każdym razem robił dereferencji na obiektach od góry do dołu, tylko zrobi to raz i będzie przechowywał potem adres w rejestrze.

Tak Obiekt niczym się nie różni od struktury pamięciowo, jedynie jak masz virtualną tablicę to masz jedną per klasę dodatkową strukturę, ale bez tego byś musiał sobie gdzieś w tablicy te adresy funkcji przechowywać, żeby móc je dynamicznie wybierać to i tak tyle samo pamięci się zużyje.

Tak jak będziesz korzystał z abstrakcji, które zapewniają bezpieczeństwo, enkapsulując je w obiektach to bez tego sam byś musiał ify porobić żeby się zabezpieczyć przed błędami więc to na to samo wychodzi.

2
KamilAdam napisał(a):

Tylko ze OOP bez dziedziczenia to zbior dobrych praktyk ktory rownie dobrze mozna stosowac do FP/Haskella czy Rusta :P

Jakie to praktyki?

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

Tylko ze OOP bez dziedziczenia to zbior dobrych praktyk ktory rownie dobrze mozna stosowac do FP/Haskella czy Rusta :P

Skoro tak, to chyba dobrze, bo to oznacza, że to zalecenia dość uniwersalne.

A czy ja mowie ze to zle?

jarekr000000 napisał(a):
KamilAdam napisał(a):

Tylko ze OOP bez dziedziczenia to zbior dobrych praktyk ktory rownie dobrze mozna stosowac do FP/Haskella czy Rusta :P

Jakie to praktyki?

Abstrakcja, Hermetyzacja I Polimorfizm. SOLID - przynajmniej ta zrozumiala czesc XD
Tylko dziedziczenie jest dziwne, Ale Jak wyrzucisz dziedziczenie pol i ograniczysz dziedziczeni implementacji to dostaniesz Rusta. A Jak dodasz niezmiennosc - to Haskella. No to trudno zebym krytykowal OOP jesli jest zgodne z Rustem w 90% a z Haskell w 80%. Moge co najwyzej krytykowac dziedziczenie pol i niedoskonale dziedziczenie implementacji

3
KamilAdam napisał(a):

Abstrakcja, Hermetyzacja I Polimorfizm. SOLID - przynajmniej ta zrozumiala czesc XD

Hermetyzacja i Polimorfizm to też rodzaje abstrakcji :-)
Wiec strasznie to upraszcza.
SOLID to jakaś mętna kupa (w sensie definicji) - więc trudno mi się do tego odnieść.

2

Różne osoby mówiąc "programowanie obiektowe" mają na myśli różne rzeczy i kryteria odn. tego jaki kod jest obiektowy a jaki nie jest.

Bez uzgodnienia tego co na prawdę piszący mają na myśli pod tym terminem, ta rozmowa trochę mija się z celem.

0
jarekr000000 napisał(a):
KamilAdam napisał(a):

Abstrakcja, Hermetyzacja I Polimorfizm. SOLID - przynajmniej ta zrozumiala czesc XD

Hermetyzacja i Polimorfizm to też rodzaje abstrakcji :-)

Bo to byl propagandowy belkot :p

SOLID to jakaś mętna kupa (w sensie definicji) - więc trudno mi się do tego odnieść.

Z SOLID to mozna die twardo wypowiadac o DI. zreszta o DI w Haskellu to mozna by ksiazke napisac. Mamy FreeMonad, tagless final, RIO I zwykle wstrzykiwanie funkcji. I to albo jest, albo nie ma. A czy jest dobrze zrobione SOLI to mozna sie klocic godzinami co juz tu robiono

Ale co mamy innego? Bo zaraz dojdziemy do tego ze OOP w ogole nie istnieje albo jest rownowazne z dziedziczeniem?

0

Docelowo nie chcemy mieć całego kodu w głowie.

Z tego względu kod dzieli się na części / moduły tak, aby nie trzeba było znać wewnętrznych szczegółów.

OOP nie jest dobre ani złe, po prostu oferuje więcej opcji w zakresie modularyzacji.

Klasa w zestawieniu z modułem umożliwia tworzenie wielu instancji, które można przekazywać / zwracać, co w połączeniu z polimorfizmem prowadzi do ogólnego i niesprzężonego z konkretnymi zależności kodu.

Z tego względu OOP jest bardziej złożone, nie tylko wymaga świadomości jak pisać modularny kod, ale również przytłacza opcjami przez które znacznie trudniej jest pojąć jak projektować utrzymywalne moduły i klasy.

W mojej opinii (uwaga to tylko opinia, a nie jakaś święta prawda):

  1. Jak dla mnie w OOP hermetyzowanie biznesowych założeń mija się z celem. Coś ukrywamy, ale i tak w raz z rozwojem produktu trzeba zaglądać do środka, by zrozumieć i niekiedy dalej rozwijać. Taka rzecz nie stanowi abstrakcji, lecz jej przeciwieństwo. W przypadku klas ludzie regularnie przyczyniają się do tworzenia nieszczelnych, przeciekających abstrakcji, które utrudniają zrozumienie projektu. Takie projekty czytać muszę z debugerem, bo z czasem takiego kodu nie da się normalnie jak człowiek czytać.

  2. Moduły są skupione na kodzie, a nie na danych i dla mnie jest to ogromny problem. Jak jakiś kod się powtarza to ludziom uruchamia się instynkt związany z DRY, co powoduje wydzielanie kodu, uwspólnianie, traktują jak kolejną warstwę na której trzeba bazować i z której ciężko się wycofać. Potem jeszcze uelastyczniają dodatkowymi opcjami, co coraz bardziej udaremnia możliwość odejścia od narzuconego schematu. Najgorsze, że bazowanie na takiej warstwie nie tylko blokuje drogę na napisanie prostego kodu, ale również psuje nasz kod, gdy ta bazowa część również wraz z biznesowymi wymaganiami ulega zmianie.

Gdyby moduły były skupione na danych (w tym sensie, że moduł wyznacza zakres w którym konkretne dane mogą występować) wtedy powtórzenia kodu były by co prawda częstsze, ale byłoby znacznie mniej zależności, powiązań między modułami i w rezultacie łatwiej byłoby utrzymać kompatybilność w interfejsach.

Wspomniane dwa problemy nie są problemami OOP, ale jak dla mnie stanowią najczęstsze bolączki z projektami pisanymi w oparciu o OOP.

0
znowutosamo4 napisał(a):

Docelowo nie chcemy mieć całego kodu w głowie.

Z tego względu kod dzieli się na części / moduły tak, aby nie trzeba było znać wewnętrznych szczegółów.

OOP nie jest dobre ani złe, po prostu oferuje więcej opcji w zakresie modularyzacji.
Klasa w zestawieniu z modułem umożliwia tworzenie wielu instancji, które można przekazywać / zwracać, co w połączeniu z polimorfizmem prowadzi do ogólnego i niesprzężonego z konkretnymi zależności kodu.

To samo możesz powiedzieć o funkcjach.

  1. Jak dla mnie w OOP hermetyzowanie biznesowych założeń mija się z celem. Coś ukrywamy, ale i tak w raz z rozwojem produktu trzeba zaglądać do środka, by zrozumieć i niekiedy dalej rozwijać. Taka rzecz nie stanowi abstrakcji, lecz jej przeciwieństwo. W przypadku klas ludzie regularnie przyczyniają się do tworzenia nieszczelnych, przeciekających abstrakcji, które w utrudniają zrozumienie projektu.

To jest argument przeciwko niepoprawnej enkapsulacji, a nie przeciwko OOP.

  1. Moduły są skupione na kodzie, a nie na danych to ogromny problem. Jak jakiś kod się powtarza to ludziom uruchamia się instynkt związany z DRY, co powoduje wydzielanie kodu, uwspólnianie, traktują jak kolejną warstwę na której trzeba bazować i z której ciężko się wycofać. Potem jeszcze uelastyczniają dodatkowymi opcjami, co coraz bardziej udaremnia możliwość odejścia od narzuconego schematu. Najgorsze, że bazowanie na takiej warstwie nie tylko blokuje drogę na napisanie łatwego kodu, ale również rozwala nasz kod, gdy ta bazowa część również wraz z biznesowymi wymaganiami ulega zmianie.

To jest argument przeciwko niepoprawnemu stosowaniu DRY i tight-coupling, a nie przeciwko OOP.

Gdyby moduły były skupione na danych (w tym sensie, że moduł wyznacza zakres w którym konkretne dane mogą występować) wtedy powtórzenia kodu były by co prawda częstsze, ale byłoby znacznie mniej zależności, powiązań między modułami i w rezultacie łatwiej byłoby utrzymać kompatybilność w interfejsach.

To samo można powiedzieć o kodzie z funkcjami.

1
Riddle napisał(a):

Dziedziczenie to najciaśniejsza relacja jaką możesz dodać, i praktycznie nigdy nie ma powodu żeby wybrać dziedziczenie zamiast kompozycje.

jedno słowo: protected

@Riddle: wiem ze bardzo chcesz mi dać łapkę i dlatego komentarz na post zamieniłeś ;)

1

Wszyscy co piszą że obiektowość jest złaaaaa, a używanie kodu typu
obiekt->podobiekt->zrobcosik() jest jeszcze gorsze proponuje pokorzystać w kodzie z bibliotek pisanych według innego paradygmatu ;)

0

Programowanie OO przypomina trochę religię. Każda religia ma swoją filozoficzną część (np. święta księga, co można a co nie) jak i tą ludową (obrzędy, demony, święta). Ludzie uwielbiają dyskutować o części filozoficznej, gdzie tak naprawdę jedyne co ma sens to dorobek ludzkości pod tym ludowym aspektem.

W samym OO nie jest ważne czym OO jest tylko jak OO jest postrzegane i jak ta filozofia programowania wpłynęła na historię. Jeśli ludzie stojący za popularyzacją terminu OO uznali, że hermetyzacja/dziedziczenie/klasy/metody wirtualne to OO a lud to kupił (nie ważne, czy ma to sens czy nie) to tak jest. Nie zmienia to faktu, że nic praktycznie nie trzyma się kupy (tak samo jak w przypadku religii)

0
Miang napisał(a):

Wszyscy co piszą że obiektowość jest złaaaaa, a używanie kodu typu
obiekt->podobiekt->zrobcosik() jest jeszcze gorsze proponuje pokorzystać w kodzie z bibliotek pisanych według innego paradygmatu ;)

Czyli Ty myślisz że paradygmat obiektowy polega po prostu na używaniu obiektów?

0
Riddle napisał(a):
Miang napisał(a):

Wszyscy co piszą że obiektowość jest złaaaaa, a używanie kodu typu
obiekt->podobiekt->zrobcosik() jest jeszcze gorsze proponuje pokorzystać w kodzie z bibliotek pisanych według innego paradygmatu ;)

Czyli Ty myślisz że paradygmat obiektowy polega po prostu na używaniu obiektów?

a skąd ten pomysł? ja nie piszę w javie ;)

0

Programowanie obiektowe to jest interfejs wystawiany na zewnątrz i używany w programie. Najistotniejsze jest to, co jest pod spodem, tzn. jak przepływają dane w systemie obiektów. A mogą one być również przekazywane w systemie programowania nałożonym na programowanie obiektowe, nakładającym jakieś określone zasady szczególne, na przykład w połączeniu z przepływem wykonania programu.
Jeśli używać niepotrzebnie obiektowości w całym programie (w szczególności wewnątrz obiektów), to tworzy się podawane w postach wcześniej serie wywołań zależnych, które kosztują zarówno miejsce w pamięci na wskaźnik jak i odwołanie przez ten wskaźnik. Autor filmu podanego w poście autora wątku właśnie sugeruje, by spłaszczać takie wywołania w stylu proceduralnym (przy okazji ukazując łamanie obiektowości przez użycie wielokrotne obiektu).
Ale tutaj mamy też podział obiektów na instancje i kolekcje. W przypadku tych drugich nie ma wielokrotnego użycia instancji obiektu (czyli w systemie w pełni obiektowym).
Przy obiektowości w całym programie pojawia się również wymieniony w tym filmie problem mnożenia klas dla różnych konfiguracji zachowań z danymi.
Poruszonych zostało wiele błędów podejścia obiektowego, ale niewiele rozwiązań tych problemów.
Ogólnie to programowanie obiektowe w całym programie to ogromny narzut kosztów pamięci i czasu wykonywania programu (spędzanego na symulowaniu w przestrzeni liniowej obiektowości kopiowanych danych), ale strukturalizowanie danych w postaci obiektów ma sens w przypadku wystawiania interfejsu do użycia w programie. I tutaj pojawia się sprzeczność: odwzorowanie rzeczywistości (np. w postaci obiektów z graficznego interfejsu użytkownika) albo obiekt zachowania z danymi określonego rodzaju.

5

screenshot-20230926193213.png

0
overcq napisał(a):

Jeśli używać niepotrzebnie obiektowości w całym programie (w szczególności wewnątrz obiektów), to tworzy się podawane w postach wcześniej serie wywołań zależnych, które kosztują zarówno miejsce w pamięci na wskaźnik jak i odwołanie przez ten wskaźnik. Autor filmu podanego w poście autora wątku właśnie sugeruje, by spłaszczać takie wywołania w stylu proceduralnym (przy okazji ukazując łamanie obiektowości przez użycie wielokrotne obiektu).

poczytaj se https://www.poczytaj.pl/ksiazka/model-obiektu-w-c-stanley-b-lippman,133243

2
Escanor16 napisał(a):

screenshot-20230926193213.png

Ale czemu mialbym zmieniac twoja opinie skoro to prawda :D Moja jest taka ze wiekszosc fanbojow OOP nie wie co to FP :p

0

Jaka jest różnica w wydajności między.

Użyciem funkcji read(struct, file)
A użyciem obiektu, Storage.read(file)

W obu przypadkach kompilator wygeneruje ten sam kod, tyle że w drugim przypadku mamy prościej przy programowaniu.
W pierwszym przypadku jest jak jest do funkcji przekazujemy strukturę i nazwę pliku.
W drugim kompilator za nas wywołuje metodę z nazwą pliku, a po skompilowaniu metoda zamienia się na funkcję(obiekt, nazwa pliku) a obiekt jest po prostu strukturą, czyli wychodzi 1:1 ten sam kod.

Obie wykonują się tak samo, tyle że w drugim przypadku mamy ładnie wszystkim zarządzane, a w pierwszym przypadku musimy szukać nazw metod, które są od operowania na tej strukturze.
Gdzie łatwiej rozdzielić.

Zdarzają się skrajne przypadki gdzie będzie gorsza obiektowość, ale to specjalnie się tak implementuje, żeby ssało wszystko bo inaczej w benchmarkach się nie będzie zgadzało.
Ale tak samo można i zrobić ze strukturami w końcu obiekt to jest struktura.

Jak chcemy mieć dane liniowo w pamięci to możemy użyć obiektów, które nie alokują na heapie, a na stosie wtedy cały obiekt możemy upchnąć w jednym miejscu pamięci.
Np. std::vector w C++ alokuje na heapie i daje w zamian możliwość manipulowania wielkością struktury, a już std::array, alokuje na stosie, ale za to nie można modyfikować wielkości i można to wykorzystać, żeby dane pamięci nie były rozrzucone, ale dalej i tak są bardzo blisko na tej samej stronie pamięci.

Jak programista jest chu,jowy to i kod napisze kiepski tu raczej nie ma znaczenia jakim sposobem to zrobi.

Trzeba być pragmatycznym i wybierać rozwiązanie w zależności od problemu, a nie z góry zarzucić, że coś jest be i tego nie stosować, bo do czegoś pasuje to, a do czegoś bardziej to.

1

Bawi mnie ta dalsza wiara w magię kompilatorów gdy co chwilę wychodzą jakieś artykuły i filmiki obalające te brednie.

0
ArthurMorgan napisał(a):

Jaka jest różnica w wydajności między.

Użyciem funkcji read(struct, file)
A użyciem obiektu, Storage.read(file)

W obu przypadkach kompilator wygeneruje ten sam kod, tyle że w drugim przypadku mamy prościej przy programowaniu.

Nie zgodzę się, że w drugim przypadku mamy prościej. Jest wręcz przeciwnie. W pierwszym, aby wiedzieć co się stanie, trzeba wiedzieć tylko co robi wywoływana funkcja, natomiast w drugim - trzeba jeszcze się zorientować jakiego typu jest obiekt, na którym ta operacja jest wykonywana. Oczywiście w tym pierwszym przypadku nazwa funkcji zwykle będzie dłuższa, ale mi to pasuje.
Mogę sobie wyszukać ją grep-em w całym katalogu ze śródłami, nie potrzebuję IDE, by mi w tym pomagało, bo i bez niego jest łatwo. Za to przy stylu obiektowym, grep jest prawie bezużyteczny i bez IDE się nie wyszuka.

1

@anonimowy: Co z tego, że kompilator nie działa optymalnie? Język programowania to interface dla człowieka, żeby był w stanie napisać o co mu chodzi i się nie pogubić. Komputer nie potrzebuje języka programowania, żeby działać, ma swój kod maszynowy i mu to wystarcza. Język programowania z punktu widzenia komputera zawsze będzie nieoptymalny.

OOD jest całkiem spoko jako sposób modelowania rzeczywistości. Dzieli duży problem, na mniejsze izolowane problemy, pozwala stosować uogólnienia (abstrakcje) i je uszczegóławiać czy to przez interface'y, czy dziedziczenie. Większość problemów zaczyna się w momencie, kiedy traktuje się tę metodykę jako dogmat i stosuje się go w miejscach, w których kompletnie nie pasuje. Często zresztą w sposób ułomny.

0

W sumie przypomniało mi się, że w pythonie wszystko jest obiektem. Więc na chłopski rozum cokolwiek nie zrobisz w pythonie, może zostać podciągnięte pod programowanie obiektowe XDD

0
piotrpo napisał(a):

@anonimowy: Co z tego, że kompilator nie działa optymalnie?

To, że nie możesz napisać, że działa optymalnie.

0

W tym wątku padło tyle definicji OOP, że przybrało to formę jednego wielkiego generyka.

A to nie kończą sie tak tu zawsze dyskusje o OOP? trudno się o czymś dyskutuje co nie ma definicji. To tak jakby pomidorowa nie miała definicji i dwie osoby rozmawiają:

  • Nie lubię pomidorowej
  • Bo nie jadłeś mojej pomidorowej z czerwoną fasolą
  • Nie możesz dodawać fasoli do pomidorowej bo to już nie będzie pomidorowa
  • Jak to nie mogę, zawsze tak robię
  • No ale to już nie jest pomidorowa
  • Oczywiście że jest

Scrum i DDD mają chociaż swoje swięte księgi, a OOP czy ma swoje święte księgi?

0
KamilAdam napisał(a):

Scrum i DDD mają chociaż swoje swięte księgi, a OOP czy ma swoje święte księgi?

Ma, to te, które opisują SOLID.

0

Ja nazywam obiektowością jakąś funkcjonalność, co ma jakieś zależności, dane musi trzymać, jest to nudne i powtarzalne, musi ifami sprawdzać błędy.
I pyk enkapsulacja w jeden obiekt i nie trzeba wszystkiego pod spodem wiedzieć, dobrze użyta obiektowość sprawia, że nowa osoba próbująca dołączyć do projektu ma bardzo prostą drogą, mega łatwo ma.

Można też taką kupę zrobić kiedyś chciałem do radare2 dołączyć to taka kupa w kodzie 3 literowe nazwy funkcji wszystko imperatywne, mega dużo siedzialem w debugerze żeby cokolwiek zrozumieć.

1
piotrpo napisał(a):
KamilAdam napisał(a):

Scrum i DDD mają chociaż swoje swięte księgi, a OOP czy ma swoje święte księgi?

Ma, to te, które opisują SOLID.

SOLID to SOLID a OOP to w zasadzie nie wiadomo co. No bo SOLID można też stosować w Ruscie czy Haskellu. No chyba że uznamy iż zastosowanie SOLID implicuje kod OOP

1

@KamilAdam: Raczej ciężko stosować open/close bez dziedziczenia, interface separation bez interface'ów i LSP bez obiektów.
I ja wiem, że padały już tutaj tezyo obiektowym pisaniu w basicu, ale dla mnie cała ta obiektowość naprawę sprowadza się do przechowywania przez obiekt swojego własnego stanu i kontrolowania jego zmian. Cały modny ostatnio "paradygmat funkcyjny" to w większości języków raczej rozwinięcie niektórych narzędzi programowania obiektowego, a nie całkowite zastąpienie tego co było.

1
piotrpo napisał(a):

OOD jest całkiem spoko jako sposób modelowania rzeczywistości. Dzieli duży problem, na mniejsze izolowane problemy, pozwala stosować uogólnienia (abstrakcje) i je uszczegóławiać czy to przez interface'y, czy dziedziczenie. Większość problemów zaczyna się w momencie, kiedy traktuje się tę metodykę jako dogmat i stosuje się go w miejscach, w których kompletnie nie pasuje. Często zresztą w sposób ułomny.

I z tym stosowaniem w miejscach gdzie nie pasuje, problem jest taki, że miejsc w których pasuje, w rzeczywistości jest bardzo mało. A mamy programowanie ZORIENTOWANE obiektowo, czyli nastawione na to, aby techniki obiektowe były używane często.
To tak, jakby trener drużyny piłkarskiej obrał strategię, że gra jego drużyny będzie zorientowana na zdobywanie goli zza połowy. Każdy oglądający mecze wie, że takiego rodzaju gole czasem padają i jak jest dogodna okazja, to można spróbować. Ale zorientowanie na nie swojej gry, jest oczywistym błędem, bo zorientowana powinna być na coś, co jest dobrym wyborem częściej.

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