Upadek programowania obiektowego

1

https://bulldogjob.pl/news/550-zegnaj-programowanie-obiektowe
Co myślicie na ten temat?
Serio obiektowe jest takie złe? W jakich przypadkach lepiej pisać obiektowo w jakich strukturalnie a w jakich funkcyjne?

9

Myslimy, ze cytowanie takich stron to robienie sobie jaj ze spolecznosci. Polecam lepiej dobierac zrodla, ktore czytujesz. Od tego zrodla poziom Twojej wiedzy nie wzrosnie :P

1

za duzo to tam nie napisal, artykul troche tak do porannej kawki i do zapomnienia

3

Liczba zero też jest przereklamowana.

2

Programowanie obiektowe ma swoje wady, ale ten artykuł ich za bardzo nie opisuje, lecz pokazuje losy programisty, który boryka się z problemem, braku testów jednostkowych i integracyjnych, złej architektury, która ma zbyt zawiłe powiązania klas ze sobą.
Prawdopodobnie w programowaniu strukturalnym by narzekał na wielki chaos, ogólnego braku powiązań itp :P

3

Kolejny klikbajtowy wpis. Gość wymyślił parę przykładów na poczekaniu i na tej podstawie wyskrobał wpis. Wybrał przykłady złego projektu.

Problem diamentowy#

Błąd w logice. Kopiarka nie jest skanerem i drukarką jednocześnie. Używa skanera, żeby zeskanować dokument i przekazuje go do drukarki.

abstract class PoweredDevice {
	abstract void start();
}

class Document {}

class Scanner extends PoweredDevice {
	void start() {}
	Document scan() {}
}

class Printer extends PoweredDevice {
	void start() {}
	void print(Document) {}
}

class Copier extends PoweredDevice {
	Scanner scanner;
	Printer printer;

	void start() {}

	void copy() {
		printer.print(scanner.scan());
	}
}

Gdyby kopiarka była drukarką i skanerem jednocześnie, mógłbym zrobić tak:
copier.scan();
copier.print();
... czy oba zadania mają pójść jednocześnie? Najczęściej tak się nie da.

Fragile base class, czyli problem delikatnej klasy bazowej

Metoda powinna robić to, na co wskazuje jej nazwa. Jeśli widzę kolejka.dodaj(Element element), to spodziewam się, że zostanie dodany element do kolejki. Jeśli autor klasy bazowej zmieni działanie metody na takie, którego nazwa nie oddaje, to jest błąd w projekcie.

1
Lubię Naleśniki z Dżemem napisał(a):

Fragile base class, czyli problem delikatnej klasy bazowej

Metoda powinna robić to, na co wskazuje jej nazwa. Jeśli widzę kolejka.dodaj(Element element), to spodziewam się, że zostanie dodany element do kolejki. Jeśli autor klasy bazowej zmieni działanie metody na takie, którego nazwa nie oddaje, to jest błąd w projekcie.

W idealnym świecie programista pisze klasy przeznaczone do dziedziczenia i myśli o tym żeby były one bezpieczne. Szkoda, że ten idealny świat tak rzadko się spotyka ;)
Nazwą metod już dawno temu przestałem wierzyć. Nie raz i nie dwa zdarzało się używać "findAll", a pod spodem SQL z "LIMIT 20". W miarę ufnym można być typom, tutaj przynajmniej można wprowadzać jakieś sensowne obostrzenia.

2
DisQ napisał(a):
Lubię Naleśniki z Dżemem napisał(a):

Fragile base class, czyli problem delikatnej klasy bazowej

Metoda powinna robić to, na co wskazuje jej nazwa. Jeśli widzę kolejka.dodaj(Element element), to spodziewam się, że zostanie dodany element do kolejki. Jeśli autor klasy bazowej zmieni działanie metody na takie, którego nazwa nie oddaje, to jest błąd w projekcie.

W idealnym świecie programista pisze klasy przeznaczone do dziedziczenia i myśli o tym żeby były one bezpieczne. Szkoda, że ten idealny świat tak rzadko się spotyka ;)
Nazwą metod już dawno temu przestałem wierzyć. Nie raz i nie dwa zdarzało się używać "findAll", a pod spodem SQL z "LIMIT 20". W miarę ufnym można być typom, tutaj przynajmniej można wprowadzać jakieś sensowne obostrzenia.

Kod piszą ludzie a nie jakaś Buka z Muminków. To, że ktoś popełnił źle zaprojektowany kod i nie poniósł za do odpowiedzialności nie znaczy, że ja od razu mam zrobić to samo. Przykładów smoków kryjących się w kodzie chyba każdy z nas może wymienić całkiem sporo ;)

1

Klasyczny argument słaby kod X vs dobry kod Y, który nie ma sensu. Słaby kod zawsze jest gorszy od dobrego kodu i nie ma tu znaczenia czy któryś z nich jest funkcyjny, imperatywny czy jeszcze jakis inny. Można napisać gówniany i nieczytelny kod funkcyjny, ale trafia się to rzadziej, bo jednak FP wymaga trochę oleju w głowie, żeby się skompilowało i zadziałało.

1

Dziedziczenie to tylko narzędzie, możliwość do wykorzystania przez programistę. I jak to z narzędziem bywa, można zastosować je dobrze i wtedy ułatwia życie, nieoptymalnie i wówczas wprowadza utrudnienia, albo wręcz źle co powoduje wymierne problemy i wypadki. Samo w sobie nie ma wartościowania, podobnie jak goto, czy zmienne globalne.

5

Artykułów tłumaczonych z angielskiego słowo w słowo nie jestem w stanie czytać.

Nigdy nie zapomnę tego dnia, kiedy byłem gotowy spieniężyć obietnicę “ponownego wykorzystania”

Polimorfizm był rudym pasierbem Trójcy zorientowanej obiektowo

Serio - mózg mnie od tego boli.

Lubię Naleśniki z Dżemem napisał(a):

Fragile base class, czyli problem delikatnej klasy bazowej

Metoda powinna robić to, na co wskazuje jej nazwa. Jeśli widzę kolejka.dodaj(Element element), to spodziewam się, że zostanie dodany element do kolejki. Jeśli autor klasy bazowej zmieni działanie metody na takie, którego nazwa nie oddaje, to jest błąd w projekcie.

Nie zrozumiałeś chyba przykładu. Nie na tym polega problem delikatnej klasy bazowej. Nazwa cały czas oddaje sposób działania metody. Problem w tym, że zmiany szczegółów implementacji mogą "przeciec", zaburzając działanie klas pochodnych. I oczywiście, że jest to "błąd w projekcie". Jest to taki błąd w projekcie, że ryzyko jego wystąpienia pojawia się, gdy modelujemy zachowania przez dziedziczenie.

0
V-2 napisał(a):

Artykułów tłumaczonych z angielskiego słowo w słowo nie jestem w stanie czytać.

Nigdy nie zapomnę tego dnia, kiedy byłem gotowy spieniężyć obietnicę “ponownego wykorzystania”

Polimorfizm był rudym pasierbem Trójcy zorientowanej obiektowo

Serio - mózg mnie od tego boli.

Lubię Naleśniki z Dżemem napisał(a):

Fragile base class, czyli problem delikatnej klasy bazowej

Metoda powinna robić to, na co wskazuje jej nazwa. Jeśli widzę kolejka.dodaj(Element element), to spodziewam się, że zostanie dodany element do kolejki. Jeśli autor klasy bazowej zmieni działanie metody na takie, którego nazwa nie oddaje, to jest błąd w projekcie.

Nie zrozumiałeś chyba przykładu. Nie na tym polega problem delikatnej klasy bazowej. Nazwa cały czas oddaje sposób działania metody. Problem w tym, że zmiany szczegółów implementacji mogą "przeciec", zaburzając działanie klas pochodnych. I oczywiście, że jest to "błąd w projekcie". Jest to taki błąd w projekcie, że ryzyko jego wystąpienia pojawia się, gdy modelujemy zachowania przez dziedziczenie.

Tu jeszcze da się przyczepić. Nazwa po zmianie nie oddaje działania metody. Gdyby nazywała się addAndIncrementCounter to by oddawała :) . Niezależnie od przyczyny to jest błąd projektowy.

0
Lubię Naleśniki z Dżemem napisał(a):

Tu jeszcze da się przyczepić. Nazwa po zmianie nie oddaje działania metody. Gdyby nazywała się addAndIncrementCounter to by oddawała :) .

Wtedy poświęcamy enkapsulację, bo ogłaszamy szczegóły implementacyjne w API klasy.

Niezależnie od przyczyny to jest błąd projektowy.

Tak już świat skonstruowano, że człowiek nie jest istotą doskonałą i zawsze będzie popełniać błędy.

Dlatego warto wybierać takie podejście, które zostawia jak najmniej możliwości popełnienia błędów.

1

Większość osób pisze obiektowo w miejszy lub większy sposób, dlatego średnio więcej złego kodu jest napisana obiektowo

5

Co myślicie na ten temat?
Serio obiektowe jest takie złe? W jakich przypadkach lepiej pisać obiektowo w jakich strukturalnie a w jakich funkcyjne?

Cóż, na początku uczyłem się pisać strukturalnie, potem obiektowo, potem poznałem programowanie funkcyjne - i w rezultacie do dzisiaj mieszam paradygmaty. Te i parę innych.

Wbrew pozorom paradygmat programowania to nie jest jakaś wielka decyzja, którą trzeba poślubić na wyłączność.

Charles Scalfani
CTO / Panoramic Software:
Byłem w stanie wykorzystywać zalety dziedziczenia, enkapsulacji i polimorfizmu. Trzech filarów paradygmatu

Nie powiedziałbym, że dziedziczenie to filar obiektówki. Dla mnie dziedziczenie to pewien wzorzec projektowy. Nie nazwiemy np. wzorca fabryki filarem obiektówki, to czemu nazywamy nim dziedziczenie?). Myślę, że na obiektówkę często psioczą ludzie, którzy ją źle rozumieją, choćby przez pryzmat dziedziczenia. A potem zamiast odrzucić dziedziczenie, to odrzucają całą obiektówkę.

Od kilku dekad programuję w językach obiektowych.

Co jest kolejnym przykładem na to, że lata doświadczenia nie przekładają się 1:1 na rozwój, wiedzę czy zdolność do refleksji. Czemu autor artykułu przez tyle lat nie słyszał o czymś takim jak kompozycja i dopiero po kilku dekadach na nią wpadł (określoną w artykule jako "hierarchia zawierania". Swoją drogą nawet podoba mi się to określenie, co nie zmienia tego, że cały artykuł jest słaby)? I czemu zamiast ją połączyć z OOP, to po prostu wypieprza OOP w kosmos? (swoją drogą dziedziczenie nie musi być złe, po prostu jest strasznie nadużywane).

Fragment o enkapsulacji.

Dobre myślenie, ale nadal, nie przekreśla to w żaden sposób OOP. Można przecież:

  • działać na niemutowalnych obiektach (i żeby uniknąć klonowania całych obiektów można zastosować structural sharing i copy-on-write, czyli kopiujemy tylko to, co się zmieniło, a pozostałe podobiekty zostawiamy nieruszone).
  • kontrolować dostęp do referencji, czyli pomóc może dyscyplina i właśnie krytykowana przez autora enkapsulacja(!). Jakby myślał, jaką referencję do którego obiektu przekazuje, nie miałby problemu (mógłby np. trochę poczytać o DDD, tam są zasady, że np. referencje do obiektów muszą być ograniczone do danego agregatu).
  • zachować po prostu odrobiny zdrowego rozsądku i nie myśleć, że OOP to piaskownica, w której można robić wszystko (takie mam wrażenie, że autor artykułu ma takie podejście do programowania. Ew. pracował przez te kilka dekad z samymi słabymi programistami, którzy nie stosowali żadnych zasad).

Fragment o polimorfizmie.

Niby dobre myślenie, ale w żaden sposób nie przekreśla to OOP. Nie trzeba pisać w OOP, żeby mieć polimorfizm, ale można pisać w OOP i mieć polimorfizm (również ten "oparty na interfejsie")

Witaj, programowanie funkcyjne. Tak miło było z Tobą pracować w ciągu ostatnich kilku lat. Chociaż nadal będę czujny, żeby znowu się tak bardzo nie zawieść!

No nie! Ucieczka z jednego dogmatu do drugiego. A za kilka dekad kolejny artykuł "programowanie funkcyjne jest do d**y, wracam do OOP".

BTW oryginalna wersja artykułu: https://medium.com/@cscalfani/goodbye-object-oriented-programming-a59cda4c0e53

3

Artykuł taki od czapki.
Z drugiej strony już od dłuższego czasu dzielę programowanie na funkcyjne i imperatywne Gdzie obiektówka to taki styl w programowaniu imperatywnym. W sumie nieciekawe.
Co więcej przykład Scali pokazuje, że mieszanie OOP i FP wychodzi średnio. Chyba najwięcej psuje subtype polimorfizm, który nie dość, że jest dość biedny (jak na polimorfizm) to jeszcze rozwala kompilatorowi pracę nad inferencją typów.
Do tego posiadanie statementów w jezyku utrudnia pisanie bezpiecznego kodu i API.
Generalnie bryndza.

Z drugiej strony języki funkcyjne mają bardzo dobre wsparcie do polimorfizmu (np. na typeclass - i bije na głowę przeważnie (bo nie zawsze) subtype polimorfizm w użyteczności), enkapsulacji, przy czym enkapsulacja + immutabiity to jest moc.
Aczkolwiek to akurat są cechy konkretnych jezyków, a nie FP. (jezyki to np. haskell i FP podzbiór Scali)
Taki Clojure mimo, że czysto FP i ultra elegancki, jakoś mi nie podchodzi.

Jak będę obrabiał grafikę, robił jakiś low level to do imperatywnego chętnie wrócę, łącznie z OOP.
Ale dla kodu biznesowego to raczej średni wybór. (nadal jestem do tego przeważnie zmuszony, ale taki life, staram się max odwalać wtedy funkcyjną manianę w Kotlinie, ale widać, że ten język nie jest do tego dobry).

2
jarekr000000 napisał(a):

Jak będę obrabiał grafikę, robił jakiś low level to do imperatywnego chętnie wrócę, łącznie z OOP.
Ale dla kodu biznesowego to raczej średni wybór. (nadal jestem do tego przeważnie zmuszony, ale taki life, staram się max odwalać wtedy funkcyjną manianę w Kotlinie, ale widać, że ten język nie jet też do tego dobry).

W biznesowym najlepiej sprawdza się programowanie proceduralne. Wszyscy to wiedzą.

Przykłady:

1

No niestety, do upadku programowania obiektowego jeszcze daleko. Ta idea trzyma się zadziwiająco mocno. Argumenty krytykujące OOP pojawiały się od kiedy pamiętam i chyba nie nastąpiło żadne nasilenie tej krytyki.

Niektórzy tu argumentują, że teksty krytykujące OOP niesłusznie skupiają się na jakichś konkretnych jego elementach. Ja uważam że jednak słusznie, bo właśnie te elementy, z dziedziczeniem na czele, przez materiały propagujące programowanie obiektowe przedstawiane są właśnie jako ten największy postęp względem innych metod.

Zwalanie niepowodzeń na "słabych programistów" ma mały sens. Jeśli jakaś metodyka pozwala "słabemu programiście" znakomicie się ukrywać ze swoją słabością, a czasem wręcz pozwala mu uchodzić za bardzo bystrego, potrafiącego zbudować tak "piękne" hierarchie, których bezużyteczność jest zauważalna dopiero po dłuższym czasie, to zdecydowanie należy postrzegać to jako wadę danej metody. W każdej metodyce popełniane są błędy, ale zwykle inna jest specyfika tych błędów. W takim powiedzmy programowaniu strukturalnym, błędy biorą się z nieuwagi, pośpiechu, czasem lenistwa. Natomiast w programowaniu obiektowym najcięższe do późniejszego naprawienia błędy często wynikają właśnie z czegoś przeciwnego: nadmiaru czasu na rozmyślania, nadmiernej ambicji do popisania się umiejętnościami itp.

OOP jest groźne, bo jest narzędziem rzadko przydatnym, natomiast sprawiającym wrażenie przydatnego często, dzięki czemu programiści wpadają w pułapkę bardzo często.

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