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

Nie rozumiem sensu programowania obiektowego

Odpowiedz Nowy wątek
2019-07-29 14:57
0

Zacznę o zastrzeżenia, że nie zamierzam, tu wszczynać wojny ideologicznej. Jak piszę, że czegoś nie rozumiem, oznacza to, że czegoś nie rozumiem, a nie że rzucam formalne wyzwanie wyznawcom odmiennego paradygmatu ;) Poza tym wiem też, że jestem po prostu hobbystką i zdaję sobie z tego sprawę, więc nie trzeba mi ani przypominać ani udowadniać ;)

Teraz w ramach wprowadzenia: mam ze 20 lat praktyki w rzeźbieniu czegoś tam w necie, ale zawsze robiłam to sobie na własny rachunek i bardzo "po swojemu", co tłumaczy, że:

Nie kumam sensu obiektówki :/ Rozumiem sensowność takiego podejścia w sytuacji, kiedy faktycznie mam manipulować na jakichś obiektach (np. elementach strony internetowej czy symulacji floty statków różnej klasy od czego podobno zaczęła się cała idea). Nie mam natomiast pojęcia do czego mi to w sytuacji typowych zastosowań, do których używam kodu:

  • policzyć coś i wyświetlić / zapisać,
  • wczytać dane z pliku, policzyć i wyświetlić / zapisać,
  • obsłużyć formularz / cms na stronie www.

W ciągu ostatnich kilkunastu lat robiłam do sprawy kilka podejść. Oglądałam jakieś przykłady prezentujące definiowanie klas zwierzątek albo samochodów, ich właściwości, metod, dziedziczenia i wszystko to rozumiem. Nie rozumiem natomiast po ch... mi to :/ jak niby i po co mam tego użyć w wymienionych powyżej zastosowaniach. Tymczasem autorzy po dojściu do tego etapu, wychodzą z założenia, że już wszystko wytłumaczyli. Więc kończy się zawsze tak, że po prostu wzruszam ramionami i piszę sobie dalej "po swojemu".

Rozumiem celowość tworzenia programu jako hierarchii pudełek przekazujących sobie parametry, ale (w moim mniemaniu) podobny efekt daje się uzyskać programowaniem proceduralnym, tworząc odpowiednią strukturę funkcji.

W dyskusjach odnośnie pojawia się dosyć często kwestia dziedziczenia i argument, że to ułatwia utrzymanie dużych projektów. Być może, nie będę się spierać, nie mając praktyki w dodawaniu kolejnych cegiełek do cudzych, rozległych systemów.


I teraz pytania:

Czy komuś chciałoby się poświęcić czas na ukazanie kilku elementarnych, praktycznych (nie zaś zwierzątkowo-abstrakcyjnyc) przykładów zastosowania programowania obiektowego z wytłumaczeniem dlaczego akurat tak?

Czy paradygmat obiektowy jest czymś uniwersalnym? Bo mam wrażenie, że to co może faktycznie się sprawdzać przy dużych projektach zastosowane dla zrobienia zwykłego "Witaj świecie" albo czegoś podobnego, to zwykłe przeinżynierowanie?

Ew. wszelkie inne uwagi odnośnie moich lamerskich wywodów.


edytowany 2x, ostatnio: Freja Draco, 2019-08-10 20:59
Pokaż pozostałe 9 komentarzy
Nie, tak nie miewam. Ogólnie jestem bardziej nastawiony na poprawianie tekstu niż "obrazków"; nie wiem w sumie, czemu. Nie lubię, gdy ktoś opuszcza jakieś zasady "bez powodu" (tzn. "równie dobrze" mógłby ich nie opuścić). - Silv 2019-07-29 15:13
I gdybyś podała mi wiarygodny (dla mnie) powód, dla którego napisałaś tytuł małą literą, byłoby to dla mnie równoznaczne z napisaniem go wielką, przynajmniej w sensie formalnym. - Silv 2019-07-29 15:15
Lubię też porządek, np. w pokoju. Gdy wszystko jest na swoim miejscu. To chyba powiązane z zasadami. - Silv 2019-07-29 15:27
że ty też miewasz czasem tak, że nie możesz się skupić, bo na ścianie jest kropka i kombinujesz, jak by można ją zetrzeć? Lol. Też coś takiego mam, chociaż właśnie jeśli chodzi o literówki. - LukeJL 2019-07-29 15:30
Może po prostu nie odpowiada taki styl programowania obiektowego jaki jest w C#, Java lub Kotlin. W Rust i Go jest trochę inaczej, zakochałem się w kodzeniu w Rust, najlepiej zaprojektowany język jaki widziałem. - sanny 2019-07-29 15:57

Pozostało 580 znaków

2019-07-29 15:03
1

Chyba nastąpiło jakieś przesilenie, skoro napisałaś wątek na forum. :) Powiem tyle, że mam podobne wątpliwości, co Ty, i czekam na odpowiedzi. Przy okazji: zauważ, że np. w JavaScripcie funkcje są formalnie obiektami (i mogą mieć właściwości).


edytowany 1x, ostatnio: Silv, 2019-07-29 15:05

Pozostało 580 znaków

2019-07-29 15:37
13

Sens programowania obiektowego bynajmniej nie wynika z dziedziczenia (jak wiemy od czasów gof kompozycja jest preferowana ponad dziedziczenie) ale właśnie z enkapsulacji, która pozwala ukrywać szczegóły w tych pudełkach.
W programowaniu proceduralnym w każdym momencie możesz wywołać dowolną procedurę, w programowaniu obiektowym tylko te które są udostępnione na zewnątrz pudełka, na dodatek musisz mieć instancję tego pudełka o określonym interfejsie (statyczne typowanie preferowane) co dodatkowo zawęża liczbę dostępnych opcji.

I właśnie to zawężanie liczby dostępnych opcji, dzielenie większego kodu na mniejsze autonomiczne fragmenty sprawia że programowanie obiektowe ma jakiś sens. A polimorfizm na dodatek pozwala nam te pudełka podmienić bez żadnych zmian w innych fragmentach kodu korzystającego z danego pudełka.


Java to taki C# tyle że z gorszą składnią.
edytowany 1x, ostatnio: neves, 2019-07-29 16:03
ale właśnie z enkapsulacji... przecież enkapsulacja nie jest zarezerwowana tylko dla OOP .... tak samo jak , dzielenie większego kodu na mniejsze autonomiczne fragmenty i tak samo jak polimorfizm -_- - nullpt4 2019-07-30 11:11
@nullpt4: a w jakim innym typie programowania występuje polimorfizm? - Tomek Pycia 2019-07-30 11:22
Poczytałem i masz rację. Dałbym ci plusa, ale tu się nie da:) - Tomek Pycia 2019-07-30 11:40
well explained - azalut 2019-07-31 07:10

Pozostało 580 znaków

2019-07-29 15:48
8

Przy samodzielnej walce OOP nie jest niczym niezbędnym. Czasami pewne rzeczy ułatwia, ale zgadzam się z Tobą, że nieraz może spowodować więcej komplikacji i zamieszania, niż jakby to zrobić "zwyczajnie", a pchanie czegoś na siłę tylko po to, bo "wypada" jest lekkim bezsensem. Zresztą ta uwaga dotyczy także innych "modnych" haseł - jak chociażby SOLID czy wzorce projektowe.

Natomiast obiektówka ma sens przy większych projektach, kiedy pracuje więcej osób, każdy dostaje swój kawałek. Ty skupiasz się na swoim obiekcie, chowasz przed kolegami jego całą wewnętrzną logikę i wystawiasz jedynie kilka metod do manipulacji stanem obiektu. Oni wiedzą, w jaki sposób mogą się z nim komunikować, ale jednocześnie Ty jesteś zabezpieczona przed jakimś grzebaniem i przypadkowym zmienieniem czegoś przez innych członków ekipy.

Oczywiście - zaraz pewnie ludzie zaczną podawać inne plusy OOP, ale moim zdaniem to, co powyżej jest główną zaletą.

A to co piszesz - czyli wczytać dane z pliku, policzyć i wyświetlić / zapisać to jest ewidentne pomieszanie odpowiedzialności. W teorii powinny być one rozdzielone - jeden fragment kodu (czy, zgodnie z OOP - obiekt) powinien się zajmować wczytywaniem danych, inny ich obrabianiem, a jeszcze inny zapisem czy wyświetlaniem. Kod, w którym masz to pomieszane jest o wiele mnie czytelny, a także trudniej go przebudowywać/rozszerzać. Ale ponownie - jeśli sobie ktoś grzebie samodzielnie, to te wskazówki może sobie wsunąć pod swój smoczy ogonek. Póki sam się w tym łapie, niech robi jak mu wygodnie ;)


That game of life is hard to play
I'm gonna lose it anyway
The losing card I'll someday lay
So this is all I have to say
edytowany 1x, ostatnio: cerrato, 2019-07-29 15:50
Pokaż pozostałe 9 komentarzy
nie podważa prawdziwości tego twierdzenia w zakresie OOP. oczywiście nie podważa prawdziwości, ale myśle że podważa zalety :P IMHO jeśli rozmawiamy o zaletach czegokolwiek to wymieniamy/listujemy/punktujemy rzeczy które są unikalne dla danego podejścia/technologii itp i to możemy nazwać zaletami/wadami. Co myślisz? - nullpt4 2019-07-30 11:37
No nie do końca. Podkreślając zalety sportowego samochodu, podajesz jego przyspieszenie i prędkość maksymalną. I to, że Porsche jest sportowe i szybkie nie oznacza, że Ferrari także nie pojedzie fajnie. Tylko po prostu wtedy, wybierając samochód, będziesz się kierować innymi czynnikami, bo oba autka jadą szybko. Ale to, że decyzja zapadnie w oparciu o inne fakty, nie zmienia tego, że oba auta są sportowe. - cerrato 2019-07-30 11:42
jasne, ale wydaje mi się, że to jest troche mieszanie jabłek z pomarańczami. Np jesli zapytamy dlaczego porshe a nie ferrari, to ktoś może powiedzieć, że porshe jest szybkie i ładne, ale to nie są żadne argumenty/zalety w tym wypadku, bo ferreri też moze być szybkie i ładne. Trzeba by było wymienić takie rzeczy które są w porshe, a których nie ma w ferrari i to by były zalety:) Przynajmniej ja tak to widze. - nullpt4 2019-07-30 11:48
Oczywiście. Tylko tutaj raczej bardziej pasującą analogią by było "kupić rodzinnego vana czy sportowe auto" - podaję plusy sportowego (szybko jedzie), ale rzeczywiście, ten fakt nie oznacza, że tylko TEN konkretny samochód może jechać szybko, a jedynie że jest to przewaga nad rodzinnym VAN'em. Tak samo podczas rozmowy o OOP - podaję hermetyzację jako plus, ale mam świadomość, że nie jest to jedyny sposób osiągnięcia tego efektu. - cerrato 2019-07-30 12:10

Pozostało 580 znaków

2019-07-29 15:52
4

policzyć coś i wyświetlić / zapisać,
wczytać dane z pliku, policzyć i wyświetlić / zapisać,

Do takich rzeczy faktycznie OOP może być nienajlepszym paradygmatem, bo takie rzeczy lepiej się robi w kategoriach myślenia o przepływie danych

  • Excel
  • programowanie funkcyjne
  • strumienie uniksowe
  • programowanie reaktywne (np. Rx)
  • data driven programming
    itp.

Wtedy masz sobie dane, które po prostu skądś płyną, są jakoś transformowane, i gdzieś się pojawiają (czy to będzie przepływ danych z komórki do komórki w Excelu, czy wynik działania funkcji w programowaniu funkcyjnym, czy strumienie wejścia i wyjścia czy obserwable w Rx - zasada podobna).

Aczkolwiek obiektówkę również można postrzegać jako przepływ danych z jednego obiektu do drugiego (zresztą jeden z pionierów OOP - Alan Kay - nawet porównywał OOP do komórek biologicznych, które się nawzajem ze sobą komunikują za pomocą jakichś tam komunikatów).

obsłużyć formularz / cms na stronie www.

Zauważ, że przy pojedynczej kalkulacji to może nie mieć znaczenia, ale jak chcesz, żeby aplikacja z automatu ci wykonywała pewne kalkulacje i odpalała pewną logikę "sama z siebie" to przydaje się mieć jakieś gdzieś tam siedzące sobie obiekty, które będą nasłuchiwać komunikatów i wysyłać komunikaty do innych (piszę "komunikat" od strony koncepcyjnej, technicznie to może być np. wywołanie metody obiektu, albo np. odpalenie funkcji która odpowiada za zdarzenie onClick przycisku na stronie). Obsługiwanie formularzy czy innego GUI akurat dość naturalnie się robi na OOP.

Czy komuś chciałoby się poświęcić czas na ukazanie kilku elementarnych, praktycznych (nie zaś zwierzątkowo-abstrakcyjnyc) przykładów zastosowania programowania obiektowego z wytłumaczeniem dlaczego akurat tak?

Praktyczne przykłady są takie, że większość osób robi obiektówkę całkowicie źle. Skupia się na złych elementach, więc większość kodu OOP pisanego przez programistów to nieporozumienie.

  • Dziedziczenie? Cóż to jest dziedziczenie? To tylko design pattern, który można użyć w jakichś ograniczonych zastosowaniach (i który można zastąpić np. kompozycją), a nie żaden fundament OOP
  • Klasy - tak samo. To tylko design pattern, żeby móc reużywać kod sprawniej i żeby kompilator miał wygodniej, a nie żaden fundament OOP.
  • Obiekty - to tylko pojemniki na dane, samo użycie obiektów nie świadczy, że kod jest obiektowy (np. w JavaScript często się używa obiektów w sytuacjach kiedy w C++ by się użyło "struktur", w Pythonie "słownika", a w PHP "tablicy asocjacyjnej"). Przy czym nie mówię, że to źle akurat. Pojemniki na dane też są potrzebne (gorzej jeśli ktoś robi pojemniki na dane, a udaje wielką obiektówkę - taki kod można rozpoznać po tym, że do wszystkich właściwości są gettery i settery - czyli efektywnie wszystkie właściwości są publiczne, nie ma enkapsulacji. Czyli programowanie proceduralne pod płaszczykiem OOP).
  • nie wychodzi poza swój język, a to błąd, bo zamiast myśleć w kategoriach OOP to myśli kategoriami swojego języka (szczególnie jeśli ktoś ma wizję OOP jak w Javie, to już w ogóle porażka. A potem ludzie piszą proceduralnie używając klas, bo myślą, że jak coś jest na klasach to już jest OOP. Java w ogóle namieszała dużo, jeśli chodzi o opis obiektówki. Jak widzę opisy wzorców projektowych czy diagramy UML - wszystko jest pod Javę pisane i w bardziej dynamicznych językach nie musi mieć to wiele sensu).

((0b10*0b11*(0b10**0b101-0b10)**0b10+0b110)**0b10+(100-1)**0b10+0x10-1).toString(0b10**0b101+0b100);
edytowany 6x, ostatnio: LukeJL, 2019-07-29 16:01
bardzo dobrze powiedziane! :) - nullpt4 2019-07-30 11:19
dzięki :) - LukeJL 2019-07-30 11:51

Pozostało 580 znaków

2019-07-29 16:04
3

Dla mnie idę obiektowości jest dzielenie kodu na odpowiedzialności i abstrakcje. Czyli korzystasz tylko z tego co możesz (na co pozwalają Ci inne obiekty). Trochę jak w firmie, gdy prosisz kogoś by zrobił coś dla Ciebie to mu to mówisz, a nie siadasz razem z nim i klikasz razem w klawiaturę 


Spring? Ja tam wole mieć kontrole nad kodem ᕙ(ꔢ)ᕗ
Haste - mała biblioteka do testów z czasem.

Pozostało 580 znaków

2019-07-29 16:09
2

Praktyczny przykład z życia:

Istniał system do generowania plików XML na podstawie danych z bazy, teoretycznie w OOP (bo osoby które to pisały używały klas), w praktyce był to kod strukturalny. Każdy generowany plik posiadał własną klasę np. OdbiorcaPlikuWFormacieA, OdbiorcaPlikuWFormacieB itp. istniało takich klas około 15 (po 1000+ linijek).

Wszystko działało (w programowaniu wszystko można napisać strukturalnie i będzie działać), problem był tylko wtedy gdy trzeba było nanieść zmiany np.

  • Dla 3 Odbiorców chcemy żeby zamiast 1 wielkiego XML powstały "chunki" plików po max. 100 MB.

Jak to zrobić w tym kodzie "strukturalnym"? Trzeba nanieść do każdego z tych plików zmianę dot. zapisu plików, finalnie musimy zmienić 3 klasy. Co więcej każda z tych klas po swojemu otwierała pliki, zapisywała, po swojemu też pobierała dane z bazy.

Przepisałem cały kod na OOP. Teraz nie istniało 15 klas dla każdego generowanego pliku, tylko 1 fabryka + system który obsługuje te pliki.

Jak wcześniej wyglądało generowanie plików:

$odbiorcaPlikuWFormacieA = new OdbiorcaPlikuWFormacieA();
$odbiorcaPlikuWFormacieA->generuj();

$odbiorcaPlikuWFormacieB = new OdbiorcaPlikuWFormacieB();
$odbiorcaPlikuWFormacieB->wygeneruj(); // specjalnie dałem inną nazwę, bo każdy pisał to po swojemu

Jak wyglądał system po przepisaniu:

$writer = XMLWriter();
$generator = new Generator([
   new Odbiorca('A', $writer),
   new Odbiorca('B', $writer);
]);

$generator->generuj();

Teraz nanosimy zmianę, chcemy żeby generowało chunki tylko dla B:

$writer = XMLWriter(); // implementuje interfejs Writer, dzięki czemu Generator wie, ze posiada metode np. write() a co Writer robi w środku, to już nie ma znaczenia dla Generatora
$chunkWriter = new ChunkXMLWriter(); // też implementuje interfejs Writer
$generator = new Generator([
   new Odbiorca('A', $writer),
   new Odbiorca('B', $chunkWriter);
]);

$generator->generuj();

To kod bardzo uproszczony, finalnie te fabryki miały po kilkaset linijek, a konstruktory po kilka argumentów.

Ile zmieniliśmy plików? Tylko fabrykę i dopisaliśmy nowy writer.

Poczytaj wujka bob`a, on potrafi wytłumaczyć na czym to polega. Dodatkowo trzeba czytać dużo kodu który jest ładnie napisany.

edytowany 6x, ostatnio: Markuz, 2019-07-29 16:15

Pozostało 580 znaków

2019-07-29 16:21
2
neves napisał(a):

W programowaniu proceduralnym w każdym momencie możesz wywołać dowolną procedurę, w programowaniu obiektowym tylko te które są udostępnione na zewnątrz pudełka

W C możesz mieć funkcje statyczne, których nie da się wprost wywołać poza plikiem w którym się znajdują.

W C masz również "mętne wskaźniki", które pozwalają na deklaracje struktur do których możesz się dobrać tylko przez funkcje udostępnione z modułu.

@Freja Draco: w sprzedaży pojawiła się książka Object Design Style Guide która z tego co czytałem jest w miarę praktyczna, ma przykłady m.in. w C++ i PHP.
Do każdego z języków znajdziesz też pozycje polskie odnośnie OOP (patrz Helion).

W uproszczeniu najważniejsza różnica m. programowaniem proceduralnym opartym o struktury a programowaniem OOP jest taka, że w OOP masz dostępne mechanizmy kapsułkowania danych wprost w języku i dzięki temu dużo łatwiej znajduje się kod odpowiedzialny za operacje na danych elementach struktur.


Szacuje się, że w Polsce brakuje 50 tys. programistów

Pozostało 580 znaków

2019-07-29 16:33
1
vpiotr napisał(a):

W C masz również "mętne wskaźniki", które pozwalają na deklaracje struktur do których możesz się dobrać tylko przez funkcje udostępnione z modułu.
[...]
W uproszczeniu najważniejsza różnica m. programowaniem proceduralnym opartym o struktury a programowaniem OOP jest taka, że w OOP masz dostępne mechanizmy kapsułkowania danych wprost w języku i dzięki temu dużo łatwiej znajduje się kod odpowiedzialny za operacje na danych elementach struktur.

A czy znasz jakiś przykład z życia, gdzie mechanizm typu "FILE *" z C był niewystarczający, a mechanizm obiektowy coś dopomógł?

Pozostało 580 znaków

2019-07-29 16:48
2
Markuz napisał(a):

Istniał system do generowania plików XML na podstawie danych z bazy, teoretycznie w OOP (bo osoby które to pisały używały klas), w praktyce był to kod strukturalny. Każdy generowany plik posiadał własną klasę np. OdbiorcaPlikuWFormacieA, OdbiorcaPlikuWFormacieB itp. istniało takich klas około 15 (po 1000+ linijek).

Ale ja nawet pisząc proceduralnie pewnie bym tego tak nie zrobiła, tylko byłaby jedna funkcja do obsługi tych 15 rodzajów plików, funkcja odwoływałaby się do podfunkcji odpowiedzialnych za poszczególne etapy przetwarzania a zmiana obsługi trzech z nich polegałaby na zmianie parametrów wywołania i ew. zmianie / dodaniu, jakichś podfunkcji.

Tzn. zakładając, że obsługa tych 15 plików posiadała jakieś wspólne, nadające się do współdzielenia etapy i że pisałabym to z głową z myślą o późniejszej rozbudowie. Bo na szybko i "na jeden raz" to się czasem różne bzdety pisuje.


edytowany 1x, ostatnio: Freja Draco, 2019-07-29 16:59

Pozostało 580 znaków

2019-07-29 16:58
0
neves napisał(a):

W programowaniu proceduralnym w każdym momencie możesz wywołać dowolną procedurę, w programowaniu obiektowym tylko te które są udostępnione na zewnątrz pudełka, na dodatek musisz mieć instancję tego pudełka o określonym interfejsie (statyczne typowanie preferowane) co dodatkowo zawęża liczbę dostępnych opcji.

No i tego też trochę nie rozumiem. Jak pisałam, jeżeli wiem, że będę operować na zbiorze obiektów (np. statków), które mają wchodzić ze sobą w interakcje, przechowywać swoje stany, reagować na polecenia, to budowa systemu klas jest dla mnie najzupełniej logiczna.

Jeżeli jednak mam program typu: policz i wyświetl, to trochę nie ogarniam filozofii:

  • zdefiniuj klasę,
  • stwórz (pojedynczy) obiekt danej klasy,
  • wywołaj jego (być może też pojedynczą) metodę,
  • i - brawo - już mamy wynik.
    :/

Ale może właśnie w takich przypadkach nie potrzeba programowania obiektowego? - Silv 2019-07-29 16:59
No właśnie też mnie to zastanawia :) - Freja Draco 2019-07-29 17:00
Też mam inklinacje do klasyfikacji całego świata. :) - Silv 2019-07-29 17:03
wtedy jest to kod strukturalny zamknięty w słowo "class", nic wspólnego z OOP nie ma. OOP się przydaje do rozbudowanych rzeczy. - Markuz 2019-07-29 17:03
to co opisałaś to po prostu wywołanie zwykłej funkcji tylko opakowanej w klasę. Niektóre języki tego wymagają, albo po prostu ludzie mają zaćmienie umysłu i zamiast zadeklarować funkcję to robią wrapper do funkcji w postaci klasy. Tylko, że to nie ma wiele wspólnego z OOP. - LukeJL 2019-07-29 17:14
w tym przypadku raczej klasa służy bardziej jako "moduł" czy "przestrzeń nazw", a nie jako klasę w sensie OOP. - LukeJL 2019-07-29 17:15

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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

Robot: CCBot