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ą?
źródło
Odpowiedzi:
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
MagicButton
i a,MagicSlider
któ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óbMagicSliderMethods.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.
źródło
Ż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:
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
interface
s, 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
instanceof
operatora lub operatora rzutowania oraz argumenty typu dla konstruktora typu ogólnego muszą zawsze być interfejsami. Klasy można używać tylko bezpośrednio zanew
operatorem, nigdzie indziej.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 :
Rozbijmy to:
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):
(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” :
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.)
źródło
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ć?
źródło
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).
źródło
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:
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.
źródło
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.
źródło
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”.
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.
źródło
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?
źródło
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.
źródło
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.
źródło
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:
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.
źródło
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!
źródło
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 są 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.
źródło
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.
źródło
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.
źródło
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ć;
Podobnie
SetName
jest z funkcją, którą ludzie powinni wykonywać wobec danej osoby, aleSplitPersonName
jest 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ą
public
iprivate
robią;źródło