Czy naprawdę potrzebujemy języków OO do zarządzania złożonością oprogramowania?


209

To będzie bardzo nietechniczne, miękkie pytanie i nie jestem pewien, czy to właściwa platforma. Ale jestem początkującym studentem CS, więc mam nadzieję, że to tolerujecie.

W pierwszym semestrze zapoznaliśmy się z pojęciami OOP, takimi jak enkapsulacja, ukrywanie danych, modułowość, dziedziczenie itd. Za pośrednictwem Java i UML. (Java to mój pierwszy język programowania)

W moim rozumieniu OOP to sposób zarządzania złożonością oprogramowania. Ale jego zasady nie są nowe ani wyjątkowe, są w pewnym sensie uniwersalne dla wszystkich dziedzin inżynierii.

Na przykład samochód jest bardzo złożoną strukturą, której złożonością zarządza hierarchia modułowych i enkapsulowanych komponentów o dobrze zdefiniowanych zachowaniach i interfejsach.

Ale nie rozumiem powodu wprowadzenia nowego paradygmatu programowania. Myślę, że wszystkie zasady zarządzania złożonością można zrealizować za pomocą proceduralnych języków programowania. Na przykład dla modułowości możemy po prostu podzielić program na wiele małych programów, które wykonują dobrze zdefiniowane zadania, których kod jest zawarty w osobnych plikach. Programy te współdziałałyby ze sobą poprzez dobrze zdefiniowane dane wejściowe i wyjściowe. Pliki mogą być chronione (szyfrowane?) W celu uzyskania enkapsulacji. Aby ponownie użyć kodu, możemy po prostu wywołać te pliki, ilekroć są one potrzebne w nowych programach. Czy to nie oddaje wszystkiego, czym jest OOP, czy brakuje mi czegoś bardzo oczywistego?

Nie proszę o dowód, że OOP zarządza złożonością. Moim zdaniem na pewno tak. Sądzę jednak, że wszystkie zasady stosowane do zarządzania złożonością, takie jak modułowość, enkapsulacja, ukrywanie danych itp., Mogą być bardzo łatwo zaimplementowane przez języki proceduralne. Dlaczego więc tak naprawdę OOP, skoro bez niego możemy zarządzać złożonością?


41
Brakuje oddzielenia interfejsu i implementacji. Bardzo ważna jest możliwość zamiany jednej implementacji na inną w czasie wykonywania . Zasadniczo osiąga się to poprzez dynamiczne wysyłanie metod OO z dziedziczeniem. Języki proceduralne również mogą to zrobić (czytaj: void wskaźniki), ale bez bezpieczeństwa typu.
marstato,

81
W dużym stopniu idea języków i projektowania obiektowego polega właśnie na tym, aby te uniwersalne, intuicyjne koncepcje były jak najłatwiejsze do przedstawienia i odtworzenia w kodzie. Jeśli masz plan lub zestaw wskazówek, jak osiągnąć te wszystkie rzeczy bez z natury języka zorientowanego obiektowo, wówczas twoje propozycje tego, jak to zrobić, byłyby skutecznie zastosowane metodologią zorientowaną obiektowo. Rzeczywiste języki OO to tylko sposób na sformalizowanie i uproszczenie tego.
Standback

14
@RobbieDee, czy rzeczywiście przeczytałeś moje pytanie? Chodzi o to, aby zrozumieć OO na bardziej podstawowym poziomie, kwestionując pogodę, że złożonością oprogramowania można zarządzać bez OO. Nie próbuję podważać OO, nie próbuję wymyślić czegoś nowego, po prostu staram się to lepiej zrozumieć i jeśli pytanie jest tak „oczywiste”, dlaczego otrzymał doskonałą odpowiedź od Jorga?
steakexchange

12
Szyfrowanie pliku nie jest hermetyzacją. Być może przesłaniałeś treść kodu innego programisty, ale niekoniecznie chroniłeś wewnętrzne działanie kodu przed innym kodem. Oryginalny autor mógłby to zrobić przed lub po zaszyfrowaniu, jeśli sobie przypomni.
JeffO

8
Nie potrzebujesz niczego poza językiem maszynowym - pozwól programiście zapamiętać kody i sam napisać jedynki i zera. Ale posiadanie pewnego rodzaju „symbolicznego” języka jest bardzo przydatne pod względem zmniejszania błędów i zwiększania wydajności, a jak zauważył Dijkstra, język, który narzuca pewną „strukturę” (lub przynajmniej ułatwia utrzymanie „struktury”), znacznie pomaga. Języki OO mogą nie być idealną technologią, biorąc pod uwagę obecny poziom zaawansowania językowego, ale są całkiem dobre do wielu zastosowań. Chodzi o to, aby zarządzać złożonością bez przeszkadzania.
Daniel R Hicks

Odpowiedzi:


177

Pozwól mi spróbować z naprawdę niską odpowiedzią teoretyczną :)

Naprawdę pytasz: dlaczego włączyć obsługę OO (Object Orientation) bezpośrednio w języku, gdy języków proceduralnych można używać do projektowania i pisania kodu OO?

A odpowiedź brzmi: mieć standard wyrażania OO w kodzie źródłowym, aby nie skończyć z 22 różnymi implementacjami dla tej samej abstrakcji.

Na przykład załóżmy, że tworzę a MagicButtoni a, MagicSliderktóre mogą być używane w systemie interfejsu użytkownika. Potrzebuję sposobu na grupowanie metod, które mogą być używane z MagicButton, metod, które mogą być używane tylko z MagicSlider, i metod, które mogą być używane przez oba. Obiekty te mają kilka metod, ponieważ oba są obiektami Magic gui.

Mogę grupować, nazywając funkcje w specjalny sposób MagicSlider_DoSomething ..., włączając metody do określonych plików nazwanych w specjalny sposób MagicSliderMethods.XXX, lub mogę znaleźć inny specjalny sposób na zrobienie tego samego. Jeśli nie ma standardowego sposobu, aby to zrobić, zrobię to inaczej niż ty i inaczej niż ktokolwiek inny. To znacznie utrudnia udostępnianie kodu.

Tak, późna wysyłka - metody wirtualne w językach OO - mogą być zaimplementowane w językach proceduralnych, ale istnieje wiele różnych sposobów na ich wdrożenie. W zależności od tego, kto napisał kod, skończysz z różnymi implementacjami OO w tym samym programie.

Pomyśl o słabym deweloperze konserwacji. Ta osoba musi zarządzać różnymi abstrakcjami obiektów i różnymi metodami wywoływania metod wirtualnych w zależności od tego, kto napisał oryginalny kod.

Ponadto: Posiadanie abstrakcji w języku pozwala zaawansowanym edytorom kodu, takim jak Eclipse, przeprowadzać wiele analiz statycznych kodu. Na przykład Eclipse może zaoferować listę wszystkich metod, które mogą być użyte na obiekcie, a także automatyczną implementację pustych „metod TODO”. Eclispe dokładnie wie, jakie metody musi wdrożyć Twoja klasa na podstawie klas, które rozszerzasz i które interfejsy implementujesz. Byłoby to prawie niemożliwe, gdyby nie istniał standard językowy do wykonania OO.


40
Klasyczny przykład: Lua. Nie jest to natywnie OO, ale można do niego przyłączyć, ale oznacza to, że istnieje około 5 różnych, równie dobrze znanych bibliotek OO, które nie są w pełni interoperacyjne.
Kroltan

55
@steakexchange Za bardzo koncentrujesz się na absolutach. Bardzo niewiele ma „jedyny cel”. Wszystkie języki robią wiele różnych rzeczy w różnym stopniu jakości. Wybór języka oznacza wybór kompromisów, które najlepiej pasują do celu, w którym go potrzebujesz.
Tim B,

42
@nocomprende Standaryzacja abstrakcji jest prawie dosłownie tym, do czego służą języki programowania. Nawet język asemblera streszcza różnice między dziesięcioma generacjami sprzętu w ciągu półtora dekady.
David Moles,

56
@DavidMoles Standaryzowane abstrakcje dosłownie tym, jakie są języki programowania. Nie trać idealnej okazji na dosłowne użycie „dosłownie”!
Clement Cherlin,

12
Można to znormalizować. Kiedy w połowie lat 90. byłem na uniwersytecie, wykonałem dość dużo pracy w X-Windows (głównie opartym na Motif, dla tych, którzy pamiętają takie rzeczy). X-Okna rzeczywiście pozwalają realizować wszystkie funkcje orientacji obiektu w regularnych C . Gimnastyka psychiczna, aby to zrobić, była jednak dość znaczna i polegała w dużej mierze na ludziach, którzy nie zaglądali do pudełka (w którym momencie Kod Widget Schroedingera ostatecznie nie żył). Języki OO ukrywają to przed programistami, tak jak robi to zwykły kompilator dla asemblera, a życie jest łatwiejsze.
Graham

211

W pierwszym semestrze zapoznaliśmy się z pojęciami OOP, takimi jak enkapsulacja, ukrywanie danych, modułowość, dziedziczenie itd. Za pośrednictwem Java i UML. (Java to mój pierwszy język programowania)

Żadna z nich nie jest koncepcją OOP. Wszystkie istnieją poza OO, niezależnie od OO, a wiele z nich zostało nawet wymyślonych przed OO.

Jeśli więc uważasz, że o to właśnie chodzi w OO, to twój wniosek jest słuszny: możesz robić wszystkie w językach proceduralnych, ponieważ nie mają one nic wspólnego z OO .

Na przykład jeden z najważniejszych artykułów na temat modułowości dotyczy kryteriów, które należy stosować w rozkładaniu układów na moduły . Nie ma tam wzmianki o OO. (Został napisany w 1972 roku, do tego czasu OO wciąż była niejasną niszą, mimo że ma już ponad dekadę).

Podczas gdy abstrakcja danych jest ważna w OO, jest bardziej konsekwencją podstawowej funkcji OO (Messaging) niż funkcji definiującej. Ponadto bardzo ważne jest, aby pamiętać, że istnieją różne rodzaje abstrakcji danych. Dwa najpopularniejsze rodzaje abstrakcji danych, które są obecnie w użyciu (jeśli zignorujemy „brak jakiejkolwiek abstrakcji”, który prawdopodobnie nadal jest używany częściej niż pozostałe dwa połączone), to abstrakcyjne typy danych i obiekty . Tak więc, mówiąc „ukrywanie informacji”, „enkapsulację” i „abstrakcję danych”, nie powiedziałeś nic o OO, ponieważ OO jest tylko jedną formą pozyskiwania danych, a obie te rzeczy są zasadniczo różne:

  • W przypadku abstrakcyjnych typów danych mechanizmem abstrakcji jest system typów ; to system typów ukrywa implementację. (System typów niekoniecznie musi być statyczny.) W Objects implementacja jest ukryta za interfejsem proceduralnym , który nie wymaga typów. (Na przykład można go zaimplementować za pomocą zamknięć, jak to ma miejsce w ECMAScript).
  • Dzięki abstrakcyjnym typom danych instancje różnych ADT są od siebie enkapsulowane, ale instancje tego samego ADT mogą sprawdzać i uzyskiwać dostęp do wzajemnej reprezentacji i prywatnej implementacji. Obiekty są zawsze otoczone wszystkim . Tylko sam obiekt może sprawdzić swoją reprezentację i uzyskać dostęp do swojej prywatnej implementacji. Żaden inny obiekt , nawet inne obiekty tego samego typu, inne instancje tej samej klasy, inne obiekty mające ten sam prototyp, klony obiektu lub cokolwiek innego, co może to zrobić. Żaden .

Nawiasem mówiąc, oznacza to, że w Javie klasy nie są obiektowe. Dwie instancje tej samej klasy mogą uzyskać dostęp do wzajemnej reprezentacji i prywatnej implementacji. Dlatego instancje klas nie są obiektami, w rzeczywistości są instancjami ADT. Java interfaces, jednak nie dostarczają danych abstrakcji obiektowego. Innymi słowy: tylko instancje interfejsów są obiektami w Javie, instancje klas nie.

Zasadniczo w przypadku typów można używać tylko interfejsów. Oznacza to, że typy parametrów metod i konstruktorów, typy zwracanych metod, typy pól instancji, pola statyczne i pola lokalne, argument dla instanceofoperatora lub operatora rzutowania oraz argumenty typu dla konstruktora typu ogólnego muszą zawsze być interfejsami. Klasy można używać tylko bezpośrednio za newoperatorem, nigdzie indziej.

Na przykład dla modułowości możemy po prostu podzielić program na wiele małych programów, które wykonują dobrze zdefiniowane zadania, których kod jest zawarty w osobnych plikach. Programy te współdziałałyby ze sobą poprzez dobrze zdefiniowane dane wejściowe i wyjściowe. Pliki mogą być chronione (szyfrowane?) W celu uzyskania enkapsulacji. Aby ponownie użyć kodu, możemy po prostu wywołać te pliki, ilekroć są one potrzebne w nowych programach. Czy to nie oddaje wszystkiego, czym jest OOP, czy brakuje mi czegoś bardzo oczywistego?

Co opisujesz jest OO.

To naprawdę dobry sposób na myślenie o OO. W rzeczywistości dokładnie to mieli na myśli oryginalni wynalazcy OO. (Alan Kay poszedł o krok dalej: wyobrażał sobie wiele małych komputerów wysyłających do siebie wiadomości przez sieć.) To, co nazywacie „programem”, jest zwykle nazywane „obiektem”, a zamiast „połączenia” zwykle mówimy „wysłać wiadomość” „.

Orientacja obiektowa polega na przesyłaniu wiadomości (inaczej dynamicznej wysyłce ). Termin „zorientowany obiektowo” został ukuty przez dr Alana Kaya, głównego projektanta Smalltalk, i definiuje go następująco :

OOP oznacza dla mnie tylko wysyłanie wiadomości, lokalne przechowywanie, ochronę i ukrywanie procesów państwowych oraz ekstremalne późne wiązanie wszystkich rzeczy.

Rozbijmy to:

  • przesyłanie wiadomości („metoda wirtualnej wysyłki”, jeśli nie znasz Smalltalk)
  • proces stanu powinien być
    • zachowane lokalnie
    • chroniony
    • ukryty
  • ekstremalne późne wiązanie wszystkich rzeczy

Jeśli chodzi o implementację, przesyłanie komunikatów jest opóźnionym wywołaniem procedury, a jeśli wywołania procedury są opóźnione, to w czasie projektowania nie można wiedzieć, co wywołać, więc nie można przyjmować żadnych założeń dotyczących konkretnej reprezentacji stanu. Tak naprawdę tak naprawdę chodzi o przesyłanie komunikatów, późne wiązanie jest implementacją przesyłania komunikatów, a jego konsekwencją jest enkapsulacja.

Później wyjaśnił, że „ główną ideą jest„ przesyłanie wiadomości ” ” i żałuje, że nazwał to „obiektowym” zamiast „zorientowanym na wiadomości”, ponieważ termin „zorientowany obiektowo” kładzie nacisk na nieistotne rzeczy (obiekty ) i odwraca uwagę od tego, co jest naprawdę ważne (wiadomości):

Tylko delikatne przypomnienie, że na ostatnim OOPSLA trochę się starałem, aby przypomnieć wszystkim, że Smalltalk nie jest NIE tylko składnią ani biblioteką klas, nie dotyczy nawet klas. Przykro mi, że już dawno wymyśliłem termin „obiekty” w tym temacie, ponieważ wielu ludzi skupia się na mniejszym pomyśle.

Wielkim pomysłem jest „przesyłanie wiadomości” - na tym właśnie polega jądro Smalltalk / Squeak (i ​​jest to coś, czego nigdy nie ukończono w fazie Xerox PARC). Japończycy mają małe słowo - ma - na „to, co jest pomiędzy” - być może najbliższym angielskim odpowiednikiem jest „pełnoekranowy”. Kluczem do stworzenia świetnych i sprawdzalnych systemów jest znacznie więcej, aby zaprojektować sposób komunikacji modułów, a nie ich wewnętrzne właściwości i zachowania. Pomyśl o Internecie - aby żyć, (a) musi pozwalać na wiele różnych rodzajów pomysłów i realizacji, które wykraczają poza jakikolwiek pojedynczy standard, i (b) umożliwiać różne stopnie bezpiecznej interoperacyjności między tymi pomysłami.

(Oczywiście dzisiaj większość ludzi nawet nie skupia się na przedmiotach, ale na zajęciach, co jest jeszcze bardziej błędne).

Przesyłanie wiadomości ma fundamentalne znaczenie dla OO, zarówno jako metafory, jak i mechanizmu.

Jeśli wyślesz komuś wiadomość, nie wiesz, co z nią zrobi. Tylko rzeczą, jaką można zauważyć, jest ich reakcja. Nie wiesz, czy sami przetworzyli wiadomość (tj. Czy obiekt ma metodę), czy przekazali wiadomość komuś innemu (delegacja / proxy), nawet jeśli ją zrozumieli. Na tym polega enkapsulacja, na tym właśnie polega OO. Nie można nawet odróżnić serwera proxy od rzeczywistego, o ile odpowiada on oczekiwaniom.

Bardziej „nowoczesnym” terminem „przesyłanie wiadomości” jest „dynamiczna wysyłka metod” lub „wirtualne wywołanie metody”, ale traci ono metaforę i skupia się na mechanizmie.

Istnieją więc dwa sposoby spojrzenia na definicję Alana Kay: jeśli spojrzysz na nią samodzielnie, możesz zauważyć, że przesyłanie wiadomości jest w zasadzie opóźnionym wywołaniem procedury, a późne wiązanie oznacza enkapsulację, więc możemy stwierdzić, że # 1 i # 2 są w rzeczywistości zbędne, a OO polega na późnym wiązaniu.

Później wyjaśnił jednak, że ważną rzeczą jest przesyłanie wiadomości, dlatego możemy spojrzeć na to z innej strony: przesyłanie wiadomości jest opóźnione. Otóż, gdyby przesyłanie wiadomości było jedyną możliwą rzeczą, to # 3 byłoby trywialnie prawdziwe: jeśli jest tylko jedna rzecz, a ta sprawa jest spóźniona, wówczas wszystkie rzeczy są spóźnione. Po raz kolejny enkapsulacja wynika z przesyłania wiadomości.

Podobne uwagi znajdują się również w „ Understanding Data Abstraction”, zrewidowanym przez Williama R. Cooka, a także w jego Propozycji dotyczącej uproszczonych, nowoczesnych definicji „Object” i „Object Oriented” :

Dynamiczna wysyłka operacji jest podstawową cechą obiektów. Oznacza to, że wywołana operacja jest dynamiczną właściwością samego obiektu. Operacji nie można zidentyfikować statycznie i ogólnie nie ma możliwości [dokładnie] wiedzieć, jaka operacja zostanie wykonana w odpowiedzi na dane żądanie, poza uruchomieniem go. Jest to dokładnie to samo, co w przypadku pierwszorzędnych funkcji, które są zawsze wysyłane dynamicznie.

W Smalltalk-72 nie było nawet żadnych przedmiotów! Były tylko strumienie wiadomości, które zostały przeanalizowane, przepisane i przekierowane. Najpierw pojawiły się metody (standardowe sposoby parsowania i przekierowania strumieni wiadomości), później pojawiły się obiekty (grupy metod, które współużytkują pewien stan prywatny). Dziedziczenie nastąpiło znacznie później, a klasy wprowadzono jedynie jako sposób na wsparcie dziedziczenia. Gdyby grupa badawcza Kay wiedziała już o prototypach, prawdopodobnie nigdy nie wprowadziliby zajęć.

Benjamin Pierce w typach i językach programowania argumentuje, że cechą definiującą orientację obiektową jest otwarta rekurencja .

Tak więc: według Alana Kay, OO polega na przesyłaniu wiadomości. Według Williama Cooka, OO polega na dynamicznej wysyłce metod (co tak naprawdę jest tym samym). Według Benjamina Pierce'a w OO chodzi o Open Recursion, co w zasadzie oznacza, że ​​autoreferencje są dynamicznie rozwiązywane (a przynajmniej tak należy się zastanowić) lub, innymi słowy, przesyłanie wiadomości.

Jak widać, osoba, która ukuła termin „OO”, ma raczej metafizyczne spojrzenie na przedmioty, Cook ma raczej pragmatyczny pogląd, a Pierce bardzo rygorystyczny pogląd matematyczny. Ale ważne jest to, że filozof, pragmatyk i teoretyk są zgodni! Wiadomości to jeden filar OO. Kropka.

Zauważ, że nie ma tu mowy o dziedziczeniu! Dziedziczenie nie jest niezbędne dla OO. Ogólnie rzecz biorąc, większość języków OO ma jakiś sposób ponownego wykorzystania implementacji, ale niekoniecznie musi to być dziedziczenie. Może to być na przykład jakaś forma przekazania uprawnień. W rzeczywistości Traktat z Orlando omawia delegowanie jako alternatywę dla dziedziczenia oraz to, w jaki sposób różne formy delegowania i dziedziczenia prowadzą do różnych punktów projektowania w przestrzeni projektowania języków zorientowanych obiektowo. (Należy pamiętać, że w rzeczywistości nawet w językach obsługujących dziedziczenie, takich jak Java, ludzie są tak naprawdę uczeni, jak tego unikać, ponownie wskazując, że nie jest to konieczne dla OO.)


16
+100 - Obarczamy winą za rzeczy, które są niewłaściwie używane.
JeffO

55
Przepraszam, ale to bardzo nieprawdziwe Alan Kay mógł wymyślić ten termin, ale zasady obowiązywały przed Smalltalk. Programowanie obiektowe wywodzi się z Simuli, a jego styl OO nie miał nic wspólnego z „wiadomościami”. Prawie każdy język OO, który odniósł sukces, działał zgodnie z podstawowymi zasadami określonymi w Simula - tymi samymi, które widzimy w Javie - a OO w stylu Smalltalk było porażką na „rynku pomysłów” za każdym razem, gdy jest ponownie wprowadzany, ponieważ to po prostu nie działa zbyt dobrze. Nazwa była jedyną naprawdę znaczącą rzeczą, do której przyczyniła się Kay.
Mason Wheeler,

23
@steakexchange Nie. „Istotą OO” jest to, co czyni ją naprawdę wyróżniającą, to obiekty z wirtualnymi metodami. Jest powód, dla którego nikt nie używa Smalltalk: system przekazywania wiadomości działa bardzo słabo w skalach poszczególnych komputerów. Za każdym razem, gdy jakiś dobry, ale naiwny projektant języka próbuje ponownie wdrożyć zasady Smalltalk, kończy się to niepowodzeniem. (Najnowszym przykładem byłby Cel C, którego nikt by nigdy nie użyłby, gdyby Steve Jobs nie zepchnął go do gardła całej społeczności iOS. Nigdy nie znalazł żadnej przyczepności poza ekosystemem Apple, i jest ku temu powód. )
Mason Wheeler,

28
@MasonWheeler Czy mógłbyś rozwinąć swój komentarz w odpowiedzi, ponieważ masz zupełnie odmienne zdanie na temat tego, co mówi Jorg?
steakexchange

20
Warto również zauważyć, że koncepcja języków zorientowanych obiektowo bardzo ewoluowała. Te starożytne koncepcje mogą nie być tak prawdziwe w dzisiejszych czasach, gdy wiele języków porzuca stare modele i obejmuje wiele paradygmatów. Spójrzmy na przykład na C # - ten język miesza prawie wszystko pod słońcem naraz i, choć w większości nazywany językiem OO, w rzeczywistości jest mieszanką różnych paradygmatów. Dzięki temu jest to naprawdę wyraziste narzędzie dla programistów na całym świecie. Ponadto, OO oparty na klasach jest jednym z wielu równie ważnych smaków programowania OO.
T. Sar

66

Sądzę jednak, że wszystkie zasady stosowane do zarządzania złożonością, takie jak modułowość, enkapsulacja, ukrywanie danych itp., Mogą być bardzo łatwo zaimplementowane przez języki proceduralne.

Kiedy mówisz „bardzo łatwo”, wyrażasz się bardzo śmiało. Tak to czytam: „Nie widzę trudności, więc nie może być bardzo duża”. Tak sformułowane, staje się jasne, że nie pytasz „dlaczego potrzebujemy OO”, pytasz „dlaczego nie są trudności, które napotkały inne paradygmaty programowania, które prowadzą do wynalezienia OO od razu? „

Odpowiedź na to pytanie jest taka, że ​​wiele z tych trudności nie istnieje w programach, nad którymi pracujesz. Nie jesteś proszony o aktualizację 40-letniego kodu spaghetti. Nie próbujesz napisać nowego menedżera wyświetlania dla systemu operacyjnego. Nie debugujesz rozproszonych aplikacji wielowątkowych.

W przypadku wielu rodzajów programów zabawkowych, którym my CS studenci mają za zadanie pisać, równie dobrze moglibyśmy pisać je w języku BASIC lub asemblerze, jak Java lub Python. Wynika to z faktu, że złożoność zadań jest tak niska, że ​​istnieje tylko jeden programista, nie ma żadnych problemów ze starszą interoperacyjnością, wydajność nie ma znaczenia, a kod prawdopodobnie będzie uruchamiany tylko kilka razy na jednym komputerze.

Wyobraź sobie, że zabierasz studenckiego kierowcę i wjeżdżasz na zatłoczoną ulicę w godzinach szczytu, na manualnej skrzyni biegów bez synchronizatora, kierując się na strome wzgórze. Nieszczęście. Dlaczego? Nie są w stanie zarządzać poziomem złożoności wymaganym do jednoczesnego przestrzegania wszystkich reguł wymaganych przez zadanie.

Teraz wyobraź sobie tego samego ucznia, ten sam pojazd, jadącego w tempie chodzenia na pustym parkingu. Są w porządku, ponieważ ich poziom umiejętności jest odpowiedni do zadania. Nie ma presji, małe ryzyko i mogą podejmować poszczególne podzadania: start, sprzęganie, zmianę biegów, przyspieszanie, kierowanie pojedynczo.

Ten uczeń może zapytać, dlaczego mamy automatyczne skrzynie biegów, czy wykwalifikowany kierowca może robić wszystkie te rzeczy jednocześnie? Odpowiedź jest taka, że ​​wystarczająco wykwalifikowany kierowca w optymalnych warunkach nie potrzebuje automatyki. Ale nie wszyscy jesteśmy zawodowymi kierowcami w doskonałej kondycji i zazwyczaj chcemy, aby projektanci samochodu zadbali o całą tę złożoność.

Wykwalifikowany, zdyscyplinowany programista może rzeczywiście stworzyć działający system o wysokim stopniu złożoności w C lub asemblerze. Ale nie wszyscy jesteśmy Linusem Torvaldsem. Nie powinniśmy też być, aby tworzyć użyteczne oprogramowanie.

Osobiście nie jestem zainteresowany odkrywaniem wszystkich cech współczesnego języka, zanim zdołam rozwiązać ten problem. Jeśli mogę skorzystać z języka, który zawiera rozwiązania już rozwiązanych problemów, dlaczego nie miałbym?

Odwrócę więc twoje pytanie i zapytam, czy języki zapewniają wygodne funkcje, takie jak enkapsulacja i polimorfizm, dlaczego nie powinniśmy ich używać?


13
Zasadniczo możliwe jest wykonywanie OO za pomocą języka proceduralnego, ale robi to ręcznie, podczas gdy za pomocą standardowego języka OO automatyzuje się i upraszcza.
steakexchange

6
@steakexchange Prawie dokładnie to.
Tim B,

3
@steakexchange dobrym historycznym przykładem tego był model obiektowy dla X Windows w tamtych czasach. Został napisany w C, ale był oparty na systemie obiektowym. Musieliście więc przestrzegać pewnych konwencji, aby się z tym komunikować, aby wasze zajęcia dobrze się bawiły z innymi.
Ukko

7
@nocomprende Na pewno. Ale jeden może uczynić własnego komputera uruchamiać wykonując surowy dysk pisze zamiast opierania się na systemie plików, a tam będzie być naprawdę trudne do debugowania problemów w ad hoc systemu obiektu zbudowanego przez nowicjusza. Hermetyzacja, jeśli jest wykonana prawidłowo, powstrzymuje nas od celowego lub nieumyślnego wtrącania się w rzeczy, których nie powinniśmy.
Clement Cherlin,

3
Interesujące jest dla mnie to, że podane przez ciebie przykłady aplikacji korzystających z OO często nie są napisane w językach OO. 40-letnie spaghetti jest prawdopodobnie napisane w C, COBOL, FORTRAN lub REXX. Menedżer wyświetlania jest prawdopodobnie napisany w C (aczkolwiek z konwencjami OO), a wiele udanych rozproszonych systemów wielowątkowych jest napisanych w Erlang, Go lub C.
James_pic

22

To, co opisujesz, to nie OOP, to abstrakcja. Abstrakcja jest obecna we wszystkich nowoczesnych modelach projektowania, nawet tych, które nie są OOP. A OOP to bardzo specyficzny rodzaj abstrakcji.

Po pierwsze, warto zauważyć, że nie ma jednej definicji OOP, więc mogą być ludzie, którzy nie zgadzają się z tym, co określam jako OOP.

Po drugie, ważne jest, aby pamiętać, że OOP został zainspirowany tradycyjnymi modelami wzornictwa, więc podobieństwa do projektowania samochodów nie są dziełem przypadku.

Oto kilka sposobów, w jakie OOP jest bardziej dopracowany niż to, co powiedziałeś:

  • Hermetyzacja: nie chodzi tylko o posiadanie ustawionego interfejsu dla modułu (tj. Abstrakcji), chodzi o zakazanie dostępu poza tym interfejsem. W Javie dostęp do zmiennej prywatnej jest błędem kompilacji, podczas gdy w projekcie samochodu możesz (w niektórych przypadkach) używać rzeczy w sposób odmienny od zamierzonego interfejsu.

  • Dziedziczenie: To naprawdę sprawia, że ​​OOP jest wyjątkowy. Po zdefiniowaniu interfejsu możesz wprowadzić wiele rzeczy do implementacji tego interfejsu i możesz to zrobić w sposób heirarchiczny, zmieniając określone części ich implementacji, dziedzicząc wszystkie poprzednie części, znacznie zmniejszając duplikację kodu.

    Jeśli myślisz o zamkniętych elementach samochodu, to tak naprawdę nie ma odpowiednika. Nie ma dla mnie sposobu, aby zrobić sprzęt, wybierając inny sprzęt i zmieniając określoną część jego implementacji. (Przynajmniej nie sądzę, nie wiem dużo o samochodach).

  • Polimorfizm : po zdefiniowaniu interfejsu wszystko, co korzysta z tego interfejsu, powinno być nierozróżnialne z punktu widzenia dostępnych operacji i nie powinieneś wiedzieć, jakiej implementacji używa się do korzystania z interfejsu. W tym miejscu nabiera znaczenia podliczanie i zasada substytucji Liskowa .

  • Łączenie : kluczowym aspektem OOP jest ścisłe powiązanie rzeczy za pomocą tych samych operacji i rozłożenie różnych form, które mogą mieć. Dane są łączone z operacjami na tych danych. Oznacza to, że bardzo łatwo jest dodać nową formę danych (nową implementację), ale bardzo trudno jest dodać nową operację do interfejsu (ponieważ trzeba zaktualizować każdą klasę, która implementuje interfejs). Jest to w przeciwieństwie do algebraicznych typów danych w językach funkcjonalnych, w których bardzo łatwo jest dodać nową operację (po prostu piszesz funkcję, która zajmuje się wszystkimi przypadkami), ale trudno jest dodać nowy wariant (ponieważ musisz dodać nowy skrzynka dla wszystkich funkcji).


1
Dobra odpowiedź! Jedna część, z którą się nie zgadzam: rozróżnienie, które rysujesz na temat enkapsulacji, jest nieprawidłowe. Hermetyzacja zawsze oznacza „zabranianie dostępu poza tym interfejsem” - dotyczy to zarówno ustawień OOP, jak i ustawień innych niż OOP. Więc ta część nie jest tak naprawdę czymś wyjątkowym dla OOP.
DW

@DW Próbowałem to wyjaśnić, mówiąc, że nie jest to unikalne dla OOP, ale że jest to różnica między enkapsulacją a abstrakcją. Dzięki za opinie!
jmite

2
DOBRZE. Ale nadal mam inne zdanie na temat tego, co jest napisane tutaj na ten temat. Napisałeś, że „oto kilka sposobów, w których OOP jest bardziej dopracowany niż to, co powiedziałeś”, ale kapsułkowanie nie jest sposobem, w jaki OOP jest bardziej dopracowany niż to, co napisano w pytaniu. Kapsułkowanie jest tym, czym jest, w każdym paradygmacie. A tam, gdzie napisałeś: „To, co opisujesz, to nie OOP, to abstrakcja”, myślałem, że pierwotne pytanie próbowało opisać enkapsulację (nie tylko abstrakcję). Chyba zostawię ten komentarz jako inną perspektywę. Myślę, że odpowiedź jest bardzo pomocna!
DW

Dziedziczenie jest powszechną cechą, ale brakuje go w kilku ważnych językach OO.
Bradd Szonye

Dobra odpowiedź, ale IMO przesadzasz z przykładem samochodu. Silnik dla danego modelu ma dobrze zdefiniowany interfejs (wałek rozrządu, „gniazda” wspornika montażowego itp.). Możesz wymienić zwykły stary gaźnik na wtrysk paliwa, dodać turbosprężarkę itp. Bez wpływu na przekładnię. (Chociaż silnik wysokoprężny wymaga zmodyfikowanego zbiornika paliwa IIRC.) I odwrotnie, można zastąpić manualną skrzynię biegów automatyczną skrzynią biegów AFAIK, która w ogóle nie wpływa na silnik.
David

11

Czy naprawdę potrzebujemy języków OO do zarządzania złożonością oprogramowania?

Zależy to od znaczenia słowa „potrzeba”.

Jeśli „potrzeba” oznacza wymaga, nie, nie wymagamy tego.

Jeśli „potrzeba” oznacza „zapewnia silne korzyści”, powiedziałbym: „Tak”, pragniemy tego.

Duży obraz

Języki OO wiążą funkcjonalność z danymi.

Można uniknąć tego wiązania i zapisu funkcji, które przekazują wartości danych.

Ale wtedy prawdopodobnie skończysz z konstelacjami danych, które idą w parze i zaczniesz przekazywać krotki, rekordy lub słowniki danych.

I tak naprawdę to są wszystkie wywołania metod: funkcje częściowe na powiązanych zestawach danych.

Funkcja według funkcji

Funkcje OOP:

  • Dziedziczenie pozwala na ponowne użycie kodu (mixiny) i koncepcji (abstrakcyjne klasy podstawowe / interfejsy) - ale można to uzyskać, zmieniając funkcje i zmienne w podzakresie.
  • Hermetyzacja pozwala na ukrywanie informacji, dzięki czemu możemy pracować na wyższych poziomach abstrakcji - ale możesz to zrobić za pomocą plików nagłówkowych, funkcji i modułów.
  • Polimorfizm pozwala nam używać różnych typów argumentów, o ile argumenty te obsługują te same interfejsy - ale możemy to zrobić również za pomocą funkcji.

Jednak żadna z tych rzeczy nie dzieje się tak łatwo, jak w przypadku języka zorientowanego obiektowo z pierwszorzędną obsługą tych funkcji.

Bibliografia

Jest wielu krytyków OOP .

Jednak badania wydają się wskazywać, że uzyskujemy większą produktywność programisty od ponownego użycia kodu przez OOP. Jest to kontrowersyjne odkrycie, a niektórzy badacze twierdzą, że nie mogą odtworzyć tych przyrostów wydajności, biorąc pod uwagę pewne ograniczenia. (źródło)

Wniosek

Nie potrzebujemy „OOP”. Ale w niektórych przypadkach użytkownik chce OOP.

Rozumiem, że dojrzali programiści mogą być dość produktywni w stylu obiektowym. A gdy pakiety mają podstawowe obiekty z prostymi interfejsami, które są łatwe do zrozumienia, nawet nowi programiści mogą szybko stać się dość wydajni.


10

Spróbuję się streścić.

Podstawową zasadą OO jest połączenie danych i zachowania w jednej jednostce organizacyjnej (obiekcie).

To pozwala nam kontrolować złożoność i była to dość innowacyjna koncepcja, kiedy się pojawiła. Porównaj to do plików z jednej strony (czyste dane), programów, które odczytują i przetwarzają te pliki z drugiej strony (czysta logika) i wysyłają (ponownie czyste dane).

Dopiero po połączeniu tego pakietu danych i logiki, modelując jakąś rzeczywistą istotę, możesz zacząć wymieniać wiadomości, tworzyć klasy potomne, oddzielać prywatne i publiczne dane i zachowania, wdrażać zachowanie polimorficzne i wykonywać całą tę magię specyficzną dla gry OO.

Tak, OO to wielka sprawa. I nie, to nie tylko kilka starych rzeczy o fantazyjnej nazwie.

Podsumowując, patrząc na elementy, a następnie mówiąc „och, cóż, nie ma tu nic, czego wcześniej nie widziałem”, nie rozpoznaje zespołu, który zawiera innowację. Wynik to więcej niż suma jego części.


8

Nie ma „oficjalnej” definicji programowania obiektowego, a rozsądni ludzie nie zgadzają się co do tego, co tak naprawdę określa jakość OO. Niektórzy twierdzą, że przesyłanie wiadomości, inni podtytuły, inni dziedziczenia, inni łączą dane i zachowanie. To nie znaczy, że ten termin jest bez znaczenia, tylko dlatego, że nie powinieneś zbytnio angażować się w kłótnie na temat tego, czym jest prawdziwe OO.

Hermetyzacja i modułowość są bardziej podstawowymi zasadami projektowania i powinny być stosowane we wszystkich paradygmatach programowania. Zwolennicy OO nie twierdzą, że te właściwości można osiągnąć tylko za pomocą OO - tylko że OO jest szczególnie odpowiedni do osiągnięcia tego celu. Oczywiście zwolennicy innych paradygmatów, takich jak powiedzmy, programowanie funkcjonalne, twierdzą, że to samo dotyczy ich paradygmatu. W praktyce wiele udanych języków to paradygmat, a OO, funkcjonalne itp. Powinny być postrzegane raczej jako narzędzia niż „jedyna prawdziwa droga”.

Myślę, że wszystkie zasady zarządzania złożonością można zrealizować za pomocą proceduralnych języków programowania.

To prawda, ponieważ ostatecznie możesz zrobić wszystko w dowolnym języku programowania. W niektórych językach może to być po prostu łatwiejsze, ponieważ wszystkie języki mają różne mocne i słabe strony.


7

Coś, czego inne odpowiedzi nie wspomniały: stan.

Mówisz o OO jako narzędziu do zarządzania złożonością . Jaka jest złożoność? To jest niewyraźne określenie. Wszyscy mamy poczucie gestalt, co to znaczy, ale trudniej jest to określić. Możemy mierzyć złożoność cykliczną, tj. Liczbę ścieżek wykonawczych w kodzie, ale nie wiem o tym mówimy, gdy używamy OO do zarządzania złożonością.

Myślę , że mówimy o złożoności związanej ze stanem.

Istnieją dwa główne pomysły za enkapsulacją . Jedna z nich, ukrywanie szczegółów implementacji , jest dość dobrze opisana w innych odpowiedziach. Ale inny ukrywa swój stan działania . Nie grzebimy w wewnętrznych danych obiektów; przekazujemy komunikaty (lub metody wywoływania, jeśli wolisz szczegóły implementacji niż koncepcję, jak zauważył Jörg Mittag). Dlaczego?

Ludzie już wspominali, że to dlatego, że nie można zmienić wewnętrznej struktury danych bez zmiany kodu dostępu do nich, i chcesz to zrobić w jednym miejscu (metoda akcesora) zamiast 300 miejsc.

Ale dzieje się tak również dlatego, że trudno jest uzasadnić kod: kod proceduralny (czy to w języku proceduralnym, czy po prostu napisanym w tym stylu) oferuje niewielką pomoc w nakładaniu ograniczeń na mutację stanu. Wszystko może się zmienić w dowolnym momencie z dowolnego miejsca. Wywoływanie funkcji / metod może mieć upiorne działanie na odległość. Automatyczne testowanie jest trudniejsze, ponieważ powodzenie testów zależy od wartości zmiennych nielokalnych, które są szeroko dostępne / dostępne.

Pozostałe dwa duże paradygmaty programowania (OO i funkcjonalne) oferują interesujące, ale prawie diametralnie przeciwstawne rozwiązania problemu złożoności związanej ze stanem. W programowaniu funkcjonalnym próbuje się tego całkowicie uniknąć: funkcje są na ogół czyste, operacje na strukturach danych zwracają kopie zamiast aktualizować oryginał na miejscu itp.

Z drugiej strony OO oferuje narzędzia do zarządzania stanem (zamiast narzędzi do unikania go). Oprócz narzędzi na poziomie języka, takich jak modyfikatory dostępu (chronione / publiczne / prywatne), pobierające i ustawiające itp. Istnieje również szereg powiązanych konwencji, takich jak Prawo Demetera, które odradza sięganie po obiekty w celu uzyskania dostępu do danych innych obiektów .

Zauważ, że nie potrzebujesz obiektów, aby zrobić coś takiego: możesz mieć zamknięcie, które ma niedostępne dane i zwraca strukturę danych funkcji do manipulowania nim. Ale czy to nie jest przedmiot? Czy intuicyjnie nie pasuje to do naszej koncepcji tego, czym jest obiekt? A jeśli mamy tę koncepcję, to czy nie lepiej jest ją powtórzyć w języku, niż (jak mówią inne odpowiedzi) polegać na kombinatorycznej eksplozji konkurencyjnych implementacji ad-hoc?


5

Czy naprawdę potrzebujemy języków OO do zarządzania złożonością oprogramowania?

Nie. Ale mogą pomóc w wielu sytuacjach.

Przez dziesięciolecia używałem głównie jednego języka OO, ale większość mojego kodu jest w rzeczywistości ściśle proceduralna. Jednak do wszystkiego, co dotyczy GUI, używam obszernej biblioteki OO wbudowanych metod i obiektów, ponieważ ogromnie upraszcza mój kod.

Na przykład aplikacja systemu Windows używająca oryginalnego niskiego poziomu interfejsu API systemu Windows do wyświetlania formularza, przycisku i pola edycji wymaga dużo kodu, podczas gdy zamiast tego używają bibliotek obiektów dostarczanych z Visual Basic lub C # lub Delphi program malutki i trywialny. Tak więc mój kod OO jest zwykle stosunkowo mały i dotyczy GUI, podczas gdy mój kod wywoływany przez te obiekty jest zwykle znacznie większy i zwykle nie dotyczy OO (chociaż może się różnić w zależności od problemu, który próbuję rozwiązać).

Widziałem programy OO, które były zbyt skomplikowane, opierały się na skomplikowanych ezoterycznych regułach dotyczących sposobu implementacji obiektów i mogłyby być znacznie prostsze, gdyby zostały napisane bez koncepcji OO. Widziałem też coś przeciwnego: wzywające do złożenia i uproszczenia skomplikowane systemy za pomocą obiektów.

W miarę zdobywania doświadczenia, różne sytuacje wymagają różnych narzędzi i rozwiązań, a jeden rozmiar nie pasuje do wszystkich.


3

Jako osoba zaangażowana w bardzo duży projekt w całości napisany w języku C, zdecydowanie mogę powiedzieć, że odpowiedź brzmi „nie”.

Modułowość jest ważna. Ale modułowość może być zaimplementowana w praktycznie każdym przyzwoitym języku. Na przykład C obsługuje kompilację modułową, pliki nagłówkowe i typy struktur. To wystarczy na 99% przypadków. Zdefiniuj moduł dla każdego nowego abstrakcyjnego typu danych, którego potrzebujesz, i zdefiniuj funkcje do obsługi tego typu danych. Czasami chcesz wydajności i te funkcje są w pliku nagłówkowym jako funkcje wbudowane, innym razem będziesz używał standardowych funkcji. Wszystko jest niewidoczne dla użytkownika, który sposób jest wybrany.

Kompozycja wspierająca struktury. Na przykład możesz mieć zablokowaną tabelę skrótów, która składa się z blokady mutex i zwykłej tabeli skrótów. To nie jest programowanie obiektowe; nie dokonuje się żadnej podklasy. Kompozycja jest narzędziem znacznie starszym niż idea programowania obiektowego.

W przypadku 1% przypadków, w których modułowość na poziomie kompilacji nie jest wystarczająca, a potrzebujesz modułowości w czasie wykonywania, istnieje tak zwane wskaźniki funkcji. Pozwalają na indywidualne implementacje dobrze zdefiniowanego interfejsu. Zauważ, że nie jest to programowanie obiektowe w języku innym niż obiektowy. To definiuje interfejs, a następnie implementuje go. Na przykład podklasa nie jest tutaj używana.

Zastanów się być może nad najbardziej złożonym projektem typu open source. Mianowicie jądro Linux. Jest napisany w całości w języku C. Odbywa się to głównie przy użyciu standardowych narzędzi modułowych na poziomie kompilacji, w tym kompozycji, a następnie czasami, gdy potrzebna jest modułowość środowiska uruchomieniowego, do definiowania i implementowania interfejsu używane są wskaźniki funkcji.

Jeśli spróbujesz znaleźć przykład programowania obiektowego w jądrze Linuksa, jestem pewien, że znalezienie takiego przykładu jest bardzo trudne, chyba że rozszerzysz programowanie obiektowe o takie standardowe zadania, jak „zdefiniowanie interfejsu, a następnie jego wdrożenie”.

Zauważ, że nawet język programowania C obsługuje programowanie obiektowe, jeśli naprawdę go potrzebujesz. Weźmy na przykład zestaw narzędzi graficznego interfejsu użytkownika GTK. W rzeczywistości jest zorientowany obiektowo, aczkolwiek napisany w języku nie zorientowanym obiektowo. To pokazuje, że idea, że ​​potrzebujesz „języka obiektowego” jest głęboko wadliwa. Język zorientowany obiektowo nie może nic zrobić, czego nie mógłby zrobić inny język. Ponadto, jeśli jesteś ekspertem programistycznym, wiesz, jak bardzo łatwo pisać kod obiektowy w dowolnym języku. Na przykład używanie C nie jest obciążeniem.

Konkluzje są takie, że języki obiektowe są prawdopodobnie przydatne tylko dla początkujących programistów, którzy nie rozumieją, w jaki sposób koncepcja jest faktycznie wdrażana. Jednak nie chciałbym być blisko żadnego projektu, w którym programiści są takimi nowymi programistami.


1
„[...] wnioski są takie, że języki zorientowane obiektowo są prawdopodobnie przydatne tylko dla początkujących programistów, którzy nie rozumieją, w jaki sposób koncepcja jest faktycznie wdrażana.” Ciekawy. Jakie języki masz na myśli? Czy masz przykłady projektów open source napisanych w tych językach, które zakończyły się niepowodzeniem lub nie?
Vincent Savard

3
Podnosisz dobre punkty, ale twój główny pomysł jest wadliwy. Tak, możliwe jest zaimplementowanie koncepcji OO, takich jak enkapsulacja, wirtualne wysyłanie, dziedziczenie, a nawet wyrzucanie elementów bezużytecznych w języku takim jak C. Można to również zrobić w asemblerze. Nie ułatwia programowania. Programowanie, a zwłaszcza projektowanie w języku takim jak C, jest zdecydowanie trudniejsze niż w języku OO. W C musisz zmapować pojęcia do ich implementacji, w języku OO nie musisz robić tego kroku (przynajmniej nie w przypadku koncepcji OO).
fishinear

1
„[wskaźniki funkcji] pozwalają na indywidualne implementacje dobrze zdefiniowanego interfejsu. Zauważ, że nie jest to programowanie obiektowe w języku innym niż obiektowy. To jest definiowanie interfejsu, a następnie jego implementacja.” Przykro nam, ale to całkowicie błędne, ponieważ właśnie takie jest OOP. „Na przykład nie stosuje się tutaj podklasy” Podklasowanie nie jest wymaganą funkcją OOP. Zauważ na przykład, że JavaScript jest językiem zorientowanym obiektowo, który nie zawiera podklas (lub, w tym przypadku, klas ... W ogóle obiektów zawierających odwołania do funkcji).
Jules

1
Aby wyjaśnić mój ostatni komentarz, mam na myśli to, że b głównym czynnikiem odróżniającym OOP (niekoniecznie żaden konkretny język OO) od innych metod jest to, że w OOP definiujemy interfejsy, które działają abstrakcyjnie na danych bez konieczności znajomości formatu tych danych przez wiązanie implementacji interfejsów z danymi. Taki właśnie jest OOP. Metoda implementacji jest nieistotna, bez względu na to, czy jest to klasa stylu Java, obiekt JavaScript (w rzeczywistości mapa nazwy do atrybutu, którym mogą być dane lub kod), czy struktura zawierająca wskaźniki funkcji i void * dla danych.
Jules

1
„Konkluzje są takie, że języki obiektowe są prawdopodobnie przydatne tylko dla początkujących programistów, którzy nie rozumieją, w jaki sposób ta koncepcja jest rzeczywiście realizowana.”… I, szczerze mówiąc, jest to po prostu obraźliwe. Wiem dość dobrze, jak te koncepcje są wdrażane. Wykonałem już wystarczająco dużo pracy, aby być nawet biegłym. W przeszłości wdrożyłem nawet tłumacza i kompilator dla języków OO. Ale ponieważ wolę pracować w językach wyższego poziomu, które mają obiekty pierwszej klasy, muszę być początkującym programistą, z którym wolisz nie pracować ?!
Jules

2

Powodem wprowadzenia paradygmatów programowania, w tym metod obiektowych, jest ułatwienie tworzenia bardziej wyrafinowanych i wydajnych programów. W wydanym w sierpniu 1981 roku numerze Byte Magazine Daniel Ingalls , jeden z kluczowych twórców Smalltalk, zdefiniował „zorientowany obiektowo” jako obejmujący następujące możliwości:

  • automatyczne zarządzanie pamięcią masową
  • umiejętność wymiany wiadomości
  • jednolita metafora, która ma zastosowanie do wszystkich operacji języka
  • żaden komponent nie zależy od wewnętrznych elementów innego komponentu (modułowość)
  • program definiuje tylko zachowanie obiektów, a nie ich reprezentację (polimorfizm)
  • każdy element powinien pojawić się tylko w jednym miejscu (faktoring)
  • korzystanie z maszyny wirtualnej niezależnej od sprzętu
  • każdy komponent dostępny dla użytkownika powinien być dostępny do obserwacji i kontroli (zasada reaktywności)
  • nie powinno być ogólnego kontrolera (brak systemu operacyjnego)

Takie były zasady, które Ingalls określił jako główne czynniki projektowania SmallTalk-80, opracowane przez Xerox Parc Research. We wspomnianym artykule w czasopiśmie można przeczytać szczegółowy opis każdej z tych zasad i ich wkładu w paradygmat zorientowany obiektowo według Ingallsa.

Wszystkie te zasady można stosować w dowolnym języku Turinga, zarówno proceduralnym, asemblerowym, jak i innym. Są to zasady projektowania, a nie specyfikacja językowa. Język zorientowany obiektowo ma na celu ułatwienie korzystania z tych zasad podczas tworzenia oprogramowania.

Na przykład, aby przyjąć pierwszą z zasad Ingall (automatyczne zarządzanie pamięcią masową), każdy może napisać własny system automatycznego zarządzania pamięcią w języku proceduralnym, ale byłoby to dużo pracy. Podczas korzystania z języka takiego jak SmallTalk lub Java, który ma wbudowane automatyczne zarządzanie pamięcią, programista nie musi wykonywać tyle pracy, aby zarządzać pamięcią. Kompromis polega na tym, że programista uzyskuje mniejszą kontrolę nad sposobem wykorzystania pamięci. Jest więc korzyść i wada. Idea paradygmatu projektowania, takiego jak programowanie obiektowe, polega na tym, że zalety paradygmatu przeważą wady przynajmniej niektórych programistów.


Myślę, że te same zasady miałyby zastosowanie w przypadku języków specyficznych dla domeny. Te same zalety i wady ... Jedną różnicą jest to, że DSL można uczynić wystarczająco prostymi, aby użytkownicy końcowi mogli z nimi pracować, ponieważ „język” odpowiada ich zrozumieniu przestrzeni problemowej i nic więcej nie zostało uwzględnione.

0

Jednym ze sposobów zarządzania złożonością oprogramowania jest całkowite oddzielenie frameworka od pożądanych działań za pomocą języka specyficznego dla domeny . Oznacza to, że poziom kodu programowania różni się od poziomu, na którym konfigurowane są pożądane wyniki - zupełnie inny język lub system. Gdy zostanie to wykonane poprawnie, konwencjonalny kod zasadniczo staje się biblioteką, a użytkownik lub inna osoba tworząca pożądane wyniki podłącza elementy do języka skryptowego lub narzędzia do projektowania wizualnego, takiego jak generator raportów.

Do pracy wymaga to ścisłego wyznaczenia granic możliwych operacji i sposobu ich łączenia (język skryptowy lub projekt wizualny, np. Narzędzie do tworzenia formularzy). Metadane są ważnym sposobem na wyodrębnienie konfiguracji w czasie wykonywania ze szczegółów kodowania, umożliwiając systemowi obsługę szerokiego zakresu pożądanych wyników. Jeśli granice są określone i utrzymane (nie akceptując każdego nadchodzącego wniosku o rozszerzenie), możesz mieć długotrwały i solidny system, który działa dla ludzi, bez konieczności bycia programistami, aby osiągnąć to, czego chcą.

Martin Fowler napisał o tym książkę, a technika jest prawie tak stara jak samo programowanie. Można niemal powiedzieć, że wszystkie języki programowania są językami specyficznymi dla domeny, więc pomysł jest endemiczny, przeoczony, ponieważ jest tak oczywisty. Ale nadal możesz tworzyć własne narzędzia do tworzenia skryptów lub projektowania wizualnego, aby ułatwić Ci życie. Czasami uogólnienie problemu znacznie ułatwia jego rozwiązanie!


0

To bardzo dobre pytanie i czuję, że podane tu odpowiedzi nie oddały sprawiedliwości, więc pójdę dalej i dodam swoje przemyślenia.

Celem jest - zarządzanie złożonością oprogramowania . Celem nie jest „używanie języka OO”.

Nie ma „powodu” do wprowadzenia nowego paradygmatu. To stało się naturalnie, gdy kodowanie stało się bardziej dojrzałe. Bardziej sensowne jest pisanie kodu, w którym dodajemy autokar na końcu pociągu (pociąg modelowany jest za pomocą połączonej listy) zamiast dodawać nowy węzeł na końcu połączonej listy.


Kodowanie w kategoriach jednostek świata rzeczywistego jest po prostu oczywiste i poprawny sposób kodować kiedy kodowania o podmiotach świecie rzeczywistym.


Komputer może pracować z dodawaniem węzła na końcu połączonej listy tak łatwo, jak może pracować z dodawaniem dodatkowego autokaru na końcu pociągu. Ale dla ludzi łatwiej jest pracować z pociągiem i autokarem niż z połączoną listą i węzłami, chociaż gdy schodzimy poziom głęboko, pociąg jest modelowany za pomocą połączonej listy.

Ochrona lub szyfrowanie plików nie może doprowadzić do enkapsulacji. Przeciwieństwem szyfrowania jest deszyfrowanie. Przeciwieństwem enkapsulacji jest Dekapsulacja, co oznacza Dekompozycję struktur i klas w językach programowania w celu osiągnięcia lepszej wydajności. Wydajność uzyskana dzięki zmniejszeniu ruchu pamięci i unikaniu sprawdzania reguł OOP.

Dlatego możesz pisać kod, który jest zarówno zaszyfrowany, jak i dobrze zamknięty, ponieważ są to różne pojęcia.

Hermetyzacja pomaga w zarządzaniu złożonością, ponieważ jest blisko rzeczywistości.

Dlatego programuj w obiektach, ponieważ łatwiej jest ci kodować i jest szybszy dla ciebie i wszystkich innych.


0

Jedną rzeczą do zapamiętania jest to:
OOP nie dotyczy funkcji językowych; chodzi o sposób strukturyzacji kodu .

OOP to sposób myślenia i projektowania architektury twojego kodu, i można to zrobić w dowolnym języku. Dotyczy to zwłaszcza tych niskopoziomowych języków innych niż OO, które są nazywane asemblerem i C. W asemblerze można wykonywać programowanie doskonale zorientowane obiektowo, a jądro Linux napisane w C jest pod wieloma względami zorientowane obiektowo. .

To powiedziawszy, funkcje OO w języku znacznie zmniejszają ilość kodu, który musisz napisać, aby osiągnąć pożądane wyniki . Tam, gdzie musisz jawnie zdefiniować wirtualną tabelę funkcji i wypełnić ją odpowiednimi wskaźnikami funkcji w C, po prostu nic nie robisz w Javie i gotowe. Języki OO po prostu usuwają wszystko, co umożliwia cruft z kodu źródłowego, ukrywając go za ładnymi abstrakcjami na poziomie języka (jak klasy, metody, członkowie, klasy podstawowe, niejawne wywołania konstruktora / destruktora itp.).

Więc nie, nie potrzebujemy języków OO do robienia OOP. Po prostu OOP jest o wiele łatwiejsze do zrobienia z przyzwoitym językiem OO.


-1

Programowanie obiektowe to coś więcej niż moduły + enkapsulacja. Jak mówisz, możliwe jest użycie modułów + enkapsulacji w języku nie zorientowanym obiektowo (proceduralnie). OOP to coś więcej niż tylko: obejmuje obiekty i metody. Więc nie, to nie przechwytuje OOP. Zobacz np. Https://en.wikipedia.org/wiki/Object-oriented_programming lub dobre wprowadzenie do podręcznika OOP.


Dziękuję za Twoją odpowiedź. Czy możesz polecić?
steakexchange

-2

Największym powodem jest to, że w miarę, jak program staje się bardziej złożony, musisz uczynić niektóre jego części niewidocznymi z innych części, lub złożoność aplikacji, a liczba funkcji sprawi, że mózg zacznie ściekać z twoich uszu.

Wyobraźmy sobie system 100 klas, z których każda może zawierać około 20 operacji; to 2000 funkcji. Jednak spośród nich może tylko 500 to kompletne operacje, takie jak „Zapisz” i „Usuń”, a 1500 to funkcje wewnętrzne, które wymagają niewielkiej konserwacji lub pełnią pewną rolę użytkową. Rozważać;

// intentionally in a non-specific language!

setName(person, name) {
    nameParts = splitPersonName(name);
    person.firstName = nameParts[0];
    person.lastName = nameParts[1];
    person.modified = true;
}

splitPersonName(name) {
    var result = [];
    result.add(name.substring(0, name.indexOf(" ")));
    result.add(name.substring(name.indexOf(" ") + 1));
    return result;
}

Podobnie SetNamejest z funkcją, którą ludzie powinni wykonywać wobec danej osoby, ale SplitPersonNamejest to funkcja użyteczności używana przez tę osobę.

Proste programowanie proceduralne nie rozróżnia tych dwóch operacji. Oznacza to, że 2000 Twoich funkcjonalnych rywalizuje o twoją uwagę. Jeśli jednak moglibyśmy oznaczyć te funkcje jako „dostępne dla każdego, kto ma rekord osoby” i „używany tylko jako funkcja użyteczności wewnątrz rekordu osoby”, wówczas nasza uwaga jest teraz 500 funkcji „dostępna dla wszystkich”, a 15 „użyteczność” funkcje dla klasy, którą edytujesz.

Tak robią publici privaterobią;

public class Person {
    public void setName(...) {...}
    private string[] splitPersonName(...) { ...}
}

1
Nie jestem pewien, czy dobrze zrozumiałeś moje pytanie. Jestem już świadomy enkapsulacji i ukrywania danych jako sposobu zarządzania złożonością. Po prostu myślę, że można to łatwo zrobić w językach proceduralnych, dzieląc program na moduły, które wykonują dobrze zdefiniowane proste zadania, których wewnętrzne działanie jest określone w osobnych chronionych źródłach. Dlaczego więc OOP, skoro możemy to robić w językach proceduralnych? To jest moje pytanie.
steakexchange

2
Myślę, że moja odpowiedź brzmi: jeśli chcesz zrobić coś takiego, będziesz musiał napisać specjalne konstrukcje narzędziowe i językowe, aby to zrobić (na przykład specjalne wywołanie „włącz” do pliku dołączania, który może nie zawierać być uwzględnione gdzie indziej). Po rozpoczęciu tej ścieżki faktycznie zacząłeś implementować język obiektowy na swój własny sposób. Na przykład C ++ był pierwotnie preprocesorem, który produkował zwykły C , i podejrzewam, że po wdrożeniu systemu wygląda on podobnie do C ++ na C.
user62575

3
@steakexchange Należy pamiętać, że OOP został opracowany w czasach, w których wiele języków proceduralnych nie miało takich modułów. Niektórzy nawet nie nazwaliby takich języków proceduralnymi. Rzeczywiście, nic nie mówi, że językiem proceduralnym nie może być OO ani vice versa. Uważaj na etykiety - mogą cię łatwo wprowadzić w błąd. Jeśli Twój język proceduralny obsługuje moduły, które mają pola i procedury publiczne i prywatne, to dobrze dla Ciebie :) Główna różnica między „tradycyjnymi procedurami” a „OOP” polega na tym, że wysyłanie połączeń jest bardziej elastyczne w OOP - w ścisłym OOP ty nigdy nie wiem, jaki kod wywołujesz.
Luaan,

2
@steakexchange W językach rodziny ML moduły działają świetnie, a w połączeniu z lambdas dają Ci pełną moc dowolnego języka OO (w końcu funkcja jest interfejsem z jedną metodą - czyż nie jest to prawie to, co zaleca „dobry kod” chłopaki w OO?: P). Z różnych powodów wciąż są rzadziej używane niż języki proceduralne, takie jak C ++ lub Java, ale mają swój urok i wiele osób stara się edukować ludzi, jak mogą uprościć swoje życie (z większym lub mniejszym sukcesem).
Luaan,

C skutecznie ma te rzeczy. Ma moduły (pliki .c) z interfejsami (pliki .h) i może mieć publiczne (zewnętrzne) i niepubliczne (zewnętrzne) metody (funkcje). Możesz nawet mieć polimorfizm biednego człowieka z szeregiem wskaźników funkcji, nie mówię, że OO jest łatwe w C (a może, przy zdrowych zmysłach), ale hermetyzacja jest dość łatwa,
Nick Keighley
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.