Czy uważasz, że programowanie obiektowe jest rozwiązaniem złożoności. Dlaczego? Ten temat może być nieco kontrowersyjny, ale moje zamiary, aby poznać odpowiedź na pytanie Dlaczego od ekspertów tutaj!
Czy uważasz, że programowanie obiektowe jest rozwiązaniem złożoności. Dlaczego? Ten temat może być nieco kontrowersyjny, ale moje zamiary, aby poznać odpowiedź na pytanie Dlaczego od ekspertów tutaj!
Odpowiedzi:
Nie ma rozwiązania złożoności.
W „The Mythical Man-Month” Fred Brooks omawia różnicę między przypadkową i niezbędną złożonością programowania. Przypadkowa złożoność jest spowodowana przez nasze narzędzia i metody, takie jak konieczność napisania i przetestowania dodatkowego kodu w języku, ponieważ nie możemy bezpośrednio wyrazić naszych pomysłów i tym podobne. Nowe metody i techniki mogą zmniejszyć przypadkową złożoność. Mogę pisać programy szybciej i lepiej niż dwadzieścia pięć lat temu, ponieważ mam lepsze języki i narzędzia.
Podstawowa złożoność wynika z faktu, że to, co staramy się zrobić z programowaniem, jest z natury skomplikowane i że istnieje nieredukowalna złożoność. „Niezbędny” w tym kontekście oznacza „odnoszący się do istoty rzeczy”, a nie „bardzo potrzebny”.
Dlatego twierdził, że nie będzie srebrnej kuli, że pisanie oprogramowania będzie nadal trudne.
Zdecydowanie polecam przeczytanie jego książki: szczególnie polecam wydanie Silver Anniversary z dodatkowym esejem „No Silver Bullet”. W tym celu przegląda proponowane rozwiązania złożoności i rozważa ich wpływ. (Najbardziej skuteczne jest oprogramowanie do pakowania w folię termokurczliwą - napisz raz coś złożonego i sprzedaj tysiące lub miliony kopii).
Teraz programowanie obiektowe pomaga, jeśli jest właściwie wykonane, tworząc abstrakcje i ukrywając złożoność. Obiekt klasy ma określone zdefiniowane zachowanie, które możemy uzasadnić, nie dbając o złożoność implementacji. Prawidłowo napisane klasy mają niewielkie sprzężenie ze sobą, a dziel i zwyciężaj to doskonały sposób na radzenie sobie ze złożonością, jeśli możesz sobie z tym poradzić. Mają także wysoką spójność, ponieważ są zbiorem funkcji i danych, które są ze sobą ściśle powiązane.
Oczekuję, że wkrótce otrzymasz lepsze odpowiedzi, ale oto prosta:
OOP pomaga * w złożoności, modelując oprogramowanie w sposób zbliżony do sposobu, w jaki modelujemy wszystko inne na świecie. Generalnie łatwiej jest wyobrazić sobie obiekt kulki wchodzący w interakcję z obiektem na ścianie, niż wyobrażać sobie szereg procedur i struktur danych, aby zrobić to samo, ponieważ jest to bliższe interakcji ze światem rzeczywistym.
* Ponieważ nic nie może „rozwiązać” złożoności
Myślę, że obecna definicja OOP głównego nurtu nie jest dobrym rozwiązaniem do zarządzania złożonością.
Jeśli wrócisz do korzeni, uważam, że na Alana Kaya duży wpływ miało „seplenienie”.
Ponieważ Lisp nie został skorumpowany przez przyjęcie do głównego nurtu, prawdopodobnie udało mu się zachować swoje podstawowe wartości. Myślę więc, że spojrzenie na sposób, w jaki seplen rozwiązuje ten problem złożoności, może dać nam pewien wgląd i możemy go wykorzystać jako podstawę do oceny, w jaki sposób OOP jest użyteczny w radzeniu sobie ze złożonością.
Jeśli spojrzysz na koniec „Wykładu 3a: Przykład Hendersona Eschera” SICP , Hal Abelson sugeruje, że złożoność jest zarządzana nie poprzez podział zadania na mniejsze podzadania, ale poprzez tworzenie warstw abstrakcji. Na najwyższym poziomie wyrażasz rozwiązanie skomplikowanego problemu w kategoriach rozwiązania niższego poziomu abstrakcji.
Myślę, że OOP pierwotnie miał być mechanizmem tworzenia tych warstw abstrakcji.
Niestety w dzisiejszych czasach OOP jest (ab) używane do pisania kodu / struktur spaghetti.
Podam przykład: grę wieloosobową FPS.
Na najwyższym poziomie gra polega na tym, że gracze biegają po mapie i strzelają do siebie przy użyciu broni.
Na kolejnym niższym poziomie musimy porozmawiać o mapach, broni i graczach. Być może możemy mówić o nich jako o fizycznych obiektach oddziałujących w świecie gry.
Na następnym niższym poziomie możemy porozmawiać o tym, jak obiekty oddziałują fizycznie (ruch, kolizje itp.).
I tak dalej i tak dalej.
Oznacza to (a tak jakby cytowałem z SICP ..), że na każdej warstwie rozwiązujemy nie tylko konkretny konkretny problem, ale także klasę problemów, które w pewnym sensie mieszczą się w sąsiedztwie problemu, który „ próbuję rozwiązać. Jeśli więc w opisie problemu pojawi się niewielka zmiana, prawdopodobnie wymagałaby tylko niewielkiej zmiany w rozwiązaniu.
Tak więc najmądrzejszym sposobem użycia OOP jest tworzenie warstw abstrakcji, na każdym poziomie abstrakcji rozwiązuje się dany problem za pomocą „obiektów” z poziomu znajdującego się bezpośrednio poniżej.
Oto fragment, który cytowałem z wykładu: http://www.youtube.com/watch?v=CYtrfncMqHQ
Jak zwykle nie zgadzam się ze wszystkimi. Daleki od dostarczania narzędzi do zarządzania złożonością, OOP tworzy ogromną złożoność, ponieważ jest to nieodpowiedni i matematycznie fałszywy paradygmat. To dezorientuje programistów bez końca, próbując modelować rzeczy za pomocą OOP, których nie można modelować za pomocą OOP.
Moim zdaniem zasadniczą pracą tutaj jest Meyer Object Oriented Software Construction. Wyszczególnia zestaw wymagań, w tym jedno, które uważam za kluczowe: zasada otwartego i zamkniętego. To mówi, że rzecz musi być otwarta do przedłużenia, ale jednocześnie zamknięta do użytku.
Meyer czerpie orientację obiektową z tych wymagań, które są zawarte w Eiffelu. Hermetyzacja zapewnia zamknięcie, otwartość dziedziczenia, a wspomnianą „rzeczą” jest klasa.
Uważam tę pracę za dobrą naukę, ponieważ Meyer wyraźnie się mylił, a ze względu na jakość jego pracy możliwe jest wskazanie błędu i naprawienie go.
Błąd sprawia, że klasa lub typ jest jednostką modułowości. To jest złe i możliwe do udowodnienia. Nawet Meyer rozpoznał problem (zwany problemem kowariancji), że OOP nie może obsłużyć relacji arity wyższych niż jeden (to znaczy, że OOP działa dobrze dla właściwości, ale zawodzi w relacjach binarnych). W Eiffelu ten problem spowodował niesłyszalne działanie systemu czcionek!
Rozwiązanie jest dość jasne. Jednostka modułowości musi być większa niż jeden typ. Musi składać się z kilku rodzajów i powiązanych z nimi metod.
Nic dziwnego, że ten model abstrakcji jest poparty matematyczną teorią abstrakcji, mianowicie teorią kategorii: typy są obiektami kategorii, a metody (funkcje) są strzałkami.
W tym modelu reprezentacje kilku typów są znane zestawowi funkcji. Reprezentacja jest ukryta przed publicznością, więc jest to enkapsulacja, ale używamy modułów, a nie klas.
Standardowy język meta (SML) i Ocaml są oparte bezpośrednio na tym modelu. Ocaml ma także klasy i OOP: nie jest to bezużyteczne, ponieważ OOP zapewnia wysyłanie właściwości, czyli dynamiczne wiązanie. Jednak większość problemów w świecie rzeczywistym wiąże się z relacjami i nic dziwnego, że klasy nie są często używane w Ocaml.
Nic dziwnego, że w bibliotece szablonów C ++ Standard prawie wcale nie ma dziedziczenia.
Faktem jest, że OOP nie daje odpowiednich narzędzi do obsługi złożoności, nie daje nawet narzędzi do obsługi naprawdę prostych problemów, zamiast tego wprowadził w błąd i pomylił dwie generacje programistów. OOP to najbardziej zła i zła rzecz, jaka przydarzyła się programowaniu, odkąd C, Fortran i Cobol zaczęli się męczyć.
Programowanie obiektowe ma swoje korzenie, które sięgają lat 60. XX wieku. Ponieważ sprzęt i oprogramowanie stają się coraz bardziej złożone, zarządzanie często staje się problemem. Naukowcy badali sposoby utrzymania jakości oprogramowania i opracowali programowanie obiektowe częściowo w celu rozwiązania typowych problemów poprzez wyraźne podkreślenie dyskretnych jednostek logiki wielokrotnego użytku.
Program zorientowany obiektowo może zatem być postrzegany jako zbiór interaktywnych obiektów, w przeciwieństwie do konwencjonalnego modelu, w którym program jest postrzegany jako lista zadań (podprogramów) do wykonania. W OOP każdy obiekt może odbierać wiadomości, przetwarzać dane i wysyłać wiadomości do innych obiektów. Każdy obiekt może być postrzegany jako niezależna „maszyna” z odrębną rolą lub odpowiedzialnością. Działania (lub „metody”) na tych obiektach są ściśle powiązane z samym obiektem.
http://en.wikipedia.org/wiki/Object-oriented_programming
To rozdzielenie obaw, wraz z innymi cechami Orientacji Obiektowej, takimi jak polimorfizm, dziedziczenie, przekazywanie wiadomości, oddzielanie i enkapsulacja, zapewnia logiczne i koncepcyjne ramy, dzięki którym złożoność dużych programów może być zarządzana w bardzo skuteczny sposób.
Istnieje wiele rodzajów komplikacji w tworzeniu oprogramowania. Na poziomie programowania OOP stara się rozwiązać złożoność, używając obiektów i klas do modelowania problematycznej dziedziny. Znany guru powiedział, że rozwiązywanie problemów reprezentuje tylko problem, więc rozwiązaniem jest sama reprezentacja. Stąd poprzez abstrakcję za pomocą klas, enkapsulację za pomocą modyfikatorów i metod dostępu, dziedziczenie do określania relacji i ponownego wykorzystania, kompozycję w nawiązywaniu relacji i współpracę między klasami, polimorfizm jako środek upraszczający określanie różnych zachowań w podobnych obiektach, można zarządzać złożonością.
Istnieją również inne sposoby zarządzania złożonością oprogramowania, na przykład programowanie logiczne (Prolog) i funkcjonalne (Haskell).
Na poziomie wyższym niż programowanie potrzebujemy Wzorów Projektowych i Zasad, które będą prowadzić OOP. W związku z tym OOP zarządza złożonością na niskim poziomie (kodowanie), podczas gdy te metodologie, takie jak wzorce projektowe i zasady, kierują projektowaniem rozwiązania na wyższym poziomie (systemowym i aplikacyjnym) i ułatwiają zarządzanie programowaniem i złożonością.
Aby odpowiedzieć na twoje pytanie, tak, OOP to tylko rozwiązanie problemu złożoności wśród wielu innych rozwiązań. To rozwiązanie na niskim poziomie. Potrzebujemy Wzorów Projektowych i Zasad, aby prowadzić OOP na wyższym poziomie.
Programowanie obiektowe zarządza podstawową i opcjonalną złożonością, ale też nie zmniejsza.
Wolę definicję podaną przez Erica Stevena Raymonda w The Art of Unix Programming , ponieważ określa ona między niezbędną, opcjonalną i przypadkową złożonością. http://www.faqs.org/docs/artu/ch13s01.html#id2964646
OOP nie robi nic dla zasadniczej lub opcjonalnej złożoności, są one funkcją wymagań programu. Może to mieć wpływ na przypadkową złożoność, ponieważ możesz czasem stworzyć bardziej elegancki projekt za pomocą OOP. Czasami jednak projekt jest gorszy przy użyciu OOP.
Złożonych problemów nie można uprościć za pomocą technologii, można nimi zarządzać tylko za pomocą technologii.
OOP to technologia, koncepcja i sposób na rozwiązanie problemu.
OOP zapewnia narzędzia do egzekwowania projektu, który może ułatwić zarządzanie złożonością, ale równie łatwo można mieć zły projekt, który zwiększa złożoność. Innymi słowy, jeśli nie zostanie właściwie użyty, możesz mieć złożoność problemów związanych z technologią.
Należy pamiętać, że istnieje wiele innych aspektów, które decydują o sukcesie projektu (tj. Styl zarządzania projektem, definicja problemu, zarządzanie zmianami itp.). Technologia, której używasz, ma znaczenie tylko wtedy, gdy pomoże ci poradzić sobie z problemem.
W końcu programowanie obiektowe nie może być rozwiązaniem złożoności; to tylko narzędzie do zarządzania. (jeśli jest właściwie stosowany)
Orientacja obiektowa (stosowana konwencjonalnie) jest użytecznym narzędziem w wielu okolicznościach, ale nie jest wystarczającym rozwiązaniem złożoności.
W szczególności często powoduje to dużą „ przypadkową złożoność ”. Przykładami są złożoność dziedziczenia implementacji, potrzeba zapewnienia wielu „standardowych funkcji” suach jako equals () i hashCode () itp. Dobra prezentacja Stuarta Hallowaya na ten temat: „ Prostota nie jest łatwa ”
Obiekty w większości języków zawierają również wiele zmiennych stanów - które w świecie współbieżnym coraz częściej zaczynają wyglądać na kiepską decyzję projektową. Znowu interesujące wideo Richa Hickeya pokazuje rozróżnienie między tożsamością i stanem obiektu oraz tym, jak błędem może być połączenie tych dwóch obiektów.
Programowanie obiektowe jest sposobem reprezentowania problemu, niczym więcej, niczym innym. Sam w sobie nie jest mniej skomplikowany niż jakikolwiek inny paradygmat programowania. Dobrze zaprojektowany system OOP zarządza i zmniejsza złożoność, ale bardzo łatwo jest zaprojektować system, który jest znacznie bardziej złożony niż to konieczne i przeszkadza we wszystkim.
Jak często mówiono o C ++, OOP daje wystarczająco dużo liny, aby się powiesić.
Myślę, że TAK , tylko dlatego, że pozwala pokroić złożoność na małe samodzielne „bloki konstrukcyjne”, które ukrywają szczegóły, a następnie użyć ich do stworzenia potrzebnej funkcjonalności, krok po kroku, warstwa po warstwie.
Dziel i rządź.
OOP to próba rozwiązania.
Najlepszym sposobem zarządzania złożonością jest tworzenie abstrakcji. Jeśli uda mi się zamienić moje dane w użyteczne kolekcje z rozpoznawalnymi funkcjami, które działają na tych kolekcjach, mogę zacząć myśleć o kolekcjach jako o odrębnych „rzeczach”. To podstawa klas i metod. Pod tym względem odpowiednio zaprojektowany OOP może pomóc w zarządzaniu złożonością.
Gdzieś po drodze ktoś zdecydował, że możemy użyć OOP, aby rozwiązać problem ponownego użycia kodu. To znaczy, po co wymyślać koło ponownie? Jeśli ktoś wykonał wiele pracy w celu rozwiązania tego problemu, wykorzystaj to, co zrobił, dodaj poprawki, które wymaga Twój konkretny projekt i voila! Stworzyłeś potężną, wyrafinowaną aplikację ze stosunkowo niewielką pracą z twojej strony. Programiści OO mogą być bardzo produktywnymi programistami.
Efektem końcowym jest to, że współczesni programiści OO ostatecznie stają się „uczniami czarnoksiężników”, gdzie łączą kilka dużych, nieporęcznych bibliotek z kilkoma liniami „kleju” i uzyskują coś, co działa. Sortuj Trochę Większość czasu. Czy istnieją potencjalne skutki uboczne korzystania z tej biblioteki z tą biblioteką? Może. Ale kto ma czas, aby naprawdę zagłębić się w kod zawarty w tych bibliotekach? Zwłaszcza, gdy biblioteki ewoluują. W rezultacie otrzymujemy rozdęte aplikacje, w których programista potrzebował garści klas i metod z tej biblioteki, ale aplikacja musi udźwignąć wszystkie INNE rzeczy, których nie potrzebowały.
Efektem końcowym jest to, że kończysz na znacznie większej złożoności niż potrzebujesz.
Kolejny mechanizm radzenia sobie ze złożonością, którą chcesz oddzielić funkcjonalność. Chcesz wszystkie funkcje dostępu do danych w jednym miejscu. Chcesz mieć wszystkie funkcje interfejsu użytkownika w jednym miejscu. Chcesz wszystkich kontrolerów w jednym miejscu. Tworzysz więc różne klasy, które zarządzają różnymi częściami funkcjonalności. Jak na razie dobrze. I to do pewnego stopnia się skaluje; twoi programiści, którzy są wykwalifikowani w dostępie do danych, mogą pisać te klasy, twoi użytkownicy interfejsu użytkownika mogą pisać klasy interfejsu użytkownika itp. Wszystko jest dobrze i dobrze.
Dopóki nie będziesz musiał utrzymywać czegoś napisanego przez kogoś innego.
Tak, dobrze wiedzieć, że wszystkie funkcje dostępu do danych znajdują się tutaj. Ale jak je nazywa?
Ta metoda wywołuje tę metodę w tej klasie. Ale kiedy patrzę na definicję klasy, nie ma metody o takiej nazwie. Och, jest to odziedziczone po czymś o jedną lub dwie warstwy w łańcuchu spadkowym. Poczekaj minutę; ta klasa zaimplementowała interfejs? Ile różnych klas implementuje ten interfejs? I używamy skomplikowanego systemu wykonawczego (patrzę na ciebie, Spring), aby „połączyć ze sobą” instancje klas w czasie wykonywania? Gdzie można zastosować JAKĄKOLWIEK klasę, która implementuje ten interfejs?
W rezultacie powstaje wiele małych, dyskretnych metod, które wykonują precyzyjne czynności. Ale ten nazywa to w innej klasie. Który nazywa to jednym, w jeszcze innej klasie. Który nazywa to jednym, w jeszcze innej klasie. Który nazywa to jednym, w dodatkowej klasie. Który zwraca wynik określonego typu. Na której musisz wywołać metodę, aby wykonać określoną czynność. Który zwraca wynik innego typu. Itp.
Jest na to określenie: kod spaghetti.
W rezultacie powstaje bardzo złożony system potrzebny tylko do skomponowania kodu. Stąd IDE, takie jak Visual Studio, Eclipse i NetBeans. Wszystkie mają znaczną krzywą uczenia się. Rzeczywiście, wiele z nich jest w stanie enkapsulować / agregować wiele narzędzi opracowanych przez różne grupy, z których każda ma swoje własne krzywe uczenia się.
To zarządza złożonością?
Debugowanie kodu jest dwa razy trudniejsze niż jego pisanie. Powodzenia w debugowaniu niektórych z tych rzeczy. Zwłaszcza jeśli korzysta z wielu bibliotek, „połączonych razem” w czasie wykonywania przy użyciu pewnego rodzaju systemu wstrzykiwania zależności.
Podsumowując: OOP zapewnia coś, co wygląda na obiecujące narzędzie do zarządzania złożonością. Rzeczywistość jest taka, że wynikowy kod jest zwykle bardzo rozdęty (ponieważ nie można wyodrębnić tylko potrzebnych fragmentów ze wszystkich powiązanych bibliotek) i potrzebujesz wyrafinowanych narzędzi do nawigacji po kodzie. Szybko staje się koszmarem konserwacji.
IMHO, to strata netto; dodaje więcej złożoności niż eliminuje. Pozwala ci to robić rzeczy, które byłyby niezwykle trudne, a nawet niemożliwe, bez tego. Ale każdy duży projekt szybko przekształca się w niemożliwy do utrzymania bałagan.
Jeśli już wiesz, jak to działa i pamiętasz to, możesz mieć szansę na utrzymanie go.
Pamiętaj, aby stosować Prawo Eaglesona: każdy twój własny kod, którego nie przeglądałeś od sześciu miesięcy, równie dobrze mógłby napisać ktoś inny.
W pewnym stopniu...
Dlaczego? Ponieważ ułatwia to bardzo logiczną modułowość. Przynajmniej w porównaniu do programowania proceduralnego, w którym pisanie ogromnych stosów kodu spaghetti jest zbyt kuszące.
Programowanie obiektowe wydaje się pomagać nam w radzeniu sobie ze złożonością, ponieważ zmusza nas do pisania kodu w określony sposób, a nie na wiele różnych sposobów. Programowanie zorientowane na zadania jest znacznie bardziej intuicyjne i dlatego programowanie rozpoczęło się w ten sposób. Orientacja obiektowa wymaga szkolenia i praktyki, aby skutecznie rozumieć i wykorzystywać, ale ograniczając programowanie do określonej ścieżki, pozwala osobom przeszkolonym efektywnie utrzymywać napisany kod.
Nie jest bardziej logiczny ani rzeczywisty niż jakakolwiek inna metoda, to tylko sposób na skoncentrowanie się na rozwiązywaniu problemów za pomocą podobnych obiektywów. Wiele specjalizacji technicznych wykorzystuje paradygmat nieintuicyjnej sztywnej metodologii do obsługi złożoności swoich zadań.
Trzecią metodą radzenia sobie ze złożonością byłoby programowanie funkcjonalne i prawdopodobnie w przyszłości pojawią się również inne nowe metody.
myślę, że jest to raczej rozwiązanie w zakresie konserwacji, ponieważ jako programista powinieneś umieszczać metody tam, gdzie masz dane, tworząc w ten sposób model obiektowy aplikacji.
tak, jest to również rozwiązanie złożoności, udostępniając model umożliwiający „widzenie” kodu w naturalny sposób, jako obiekty posiadające właściwości i możliwe działania