Uwaga : to pytanie jest zredagowanym fragmentem posta na blogu, który napisałem kilka miesięcy temu. Po umieszczeniu linku do bloga w komentarzu do Programmers.SE ktoś poprosił o opublikowanie tutaj pytania, aby mogło na nie odpowiedzieć. Ten post jest moim najbardziej popularnym, ponieważ ludzie często piszą w Google „Nie programuję obiektowo” . Odpowiedz tutaj lub w komentarzu na Wordpress.
Co to jest programowanie obiektowe? Nikt nie dał mi zadowalającej odpowiedzi. Wydaje mi się, że nie dostaniesz dobrej definicji od kogoś, kto krąży wokół i mówi „obiekt” i „obiektowo” z nosem w powietrzu. Nie dostaniesz też dobrej definicji od kogoś, kto nie zrobił nic poza programowaniem obiektowym. Nikt, kto rozumie zarówno programowanie proceduralne, jak i obiektowe, nigdy nie dał mi spójnego wyobrażenia o tym, co faktycznie robi program zorientowany obiektowo.
Czy ktoś może mi przekazać swoje pomysły na temat zalet programowania obiektowego?
źródło
Odpowiedzi:
Pomyśl o oprogramowaniu jako o maszynie lub linii montażowej, która istnieje wewnątrz komputera. Niektóre surowce i komponenty są podawane do maszyny i zgodnie z zestawem procedur przetwarzane są w produkt końcowy. Procedury są ustawione tak, aby wykonywać określoną operację na pewnym surowcu lub elemencie zgodnie z określonym zestawem parametrów (np. Czas, temperatura, odległość itp.) W określonej kolejności. Jeśli szczegóły operacji, która ma zostać wykonana, są nieprawidłowe lub czujniki maszyny nie są poprawnie skalibrowane, lub jeśli jakiś surowiec lub komponent nie spełniał oczekiwanych standardów jakości, mogłoby to zmienić wynik operacji i produkt nie wyszedłby zgodnie z oczekiwaniami.
Taka maszyna jest bardzo sztywna w działaniu i dopuszczalnych wejściach. Maszyny nie kwestionują inteligencji projektantów ani obecnego środowiska operacyjnego. Będzie nadal postępować zgodnie z procedurami, o ile jest to skierowane. Nawet jeśli zmiana surowców lub komponentów mogłaby mieć dramatyczny wpływ na to, co wydarzyło się w późniejszych operacjach, maszyna nadal wykonywałaby swoje procedury. Proces ten musiałby zostać poddany przeglądowi, aby zobaczyć, jakie zmiany w procedurach były konieczne do skompensowania i uzyskania pożądanego rezultatu. Zmiana projektu lub konfiguracji produktu może również wymagać znacznej zmiany w wykonywanych operacjach lub ich kolejności. Chociaż osoby odpowiedzialne za produkcję szybko nauczyły się, jak ważne jest izolowanie operacji w jak największym stopniu, aby ograniczyć niepożądane efekty między nimi, przyjmuje się wiele założeń dotyczących stanu składników podczas przetwarzania; założenia, które mogą nie zostać wykryte, dopóki produkt końcowy nie znajdzie się w rękach użytkownika w innym środowisku operacyjnym.
Tak wygląda programowanie proceduralne.
To, co zapewnia orientacja obiektowa, jest sposobem na usunięcie założeń dotyczących stanu komponentów; dlatego operacje, które należy wykonać na tym elemencie i jak go zintegrować z produktem końcowym. Innymi słowy, OOP przypomina pobieranie szczegółów procesu do obsługi określonego elementu i oddawanie go mniejszej maszynie. Większa maszyna odpowiedzialna za proces informuje maszynę specyficzną dla komponentu, którą operację spodziewa się wykonać, ale pozostawia szczegóły dotyczące kroków, które ma wykonać maszyna specyficzna dla komponentu.
Co do zalet orientacji obiektowej w porównaniu z oprogramowaniem nieorientowanym obiektowo:
źródło
Z twojego bloga wygląda na to, że znasz zarówno programowanie imperatywne, jak i funkcjonalne oraz że znasz podstawowe pojęcia związane z programowaniem obiektowym, ale tak naprawdę nigdy tak naprawdę nie klikałeś tego, co czyni to użytecznym. Spróbuję wyjaśnić tę wiedzę i mam nadzieję, że będzie ona dla ciebie pomocna.
U podstaw OOP jest sposób na zastosowanie imperatywnego paradygmatu, aby lepiej zarządzać wysokim stopniem złożoności poprzez tworzenie „inteligentnych” struktur danych, które modelują dziedzinę problemową. W (standardowym proceduralnym, nieobiektywowym) programie masz dwie podstawowe rzeczy: zmienne i kod, który wie, co z nimi zrobić. Kod pobiera dane wejściowe od użytkownika i różnych innych źródeł, przechowuje je w zmiennych, operuje na nim i generuje dane wyjściowe, które trafiają do użytkownika lub różnych innych lokalizacji.
Programowanie obiektowe jest sposobem na uproszczenie programu, biorąc ten podstawowy wzorzec i powtarzając go na mniejszą skalę. Podobnie jak program to duży zbiór danych z kodem, który wie, co z nim zrobić, każdy obiekt to niewielki kawałek danych powiązany z kodem, który wie, co z nim zrobić.
Dzieląc problematyczną domenę na mniejsze części i upewniając się, że jak najwięcej danych jest związanych bezpośrednio z kodem, który wie, co z nim zrobić, znacznie ułatwiasz rozumowanie całego procesu, a także podrzędnych problemy, które składają się na proces.
Grupując dane w klasy obiektów, można scentralizować kod związany z tymi danymi, co ułatwia znajdowanie i debugowanie odpowiedniego kodu. A poprzez enkapsulację danych za specyfikatorami dostępu i dostęp do nich jedynie metodami (lub właściwościami, jeśli obsługuje je Twój język), znacznie zmniejszasz ryzyko uszkodzenia danych lub naruszenia niezmienników.
A dzięki dziedziczeniu i polimorfizmowi można ponownie wykorzystać istniejące klasy, dostosowując je do własnych potrzeb, bez konieczności modyfikowania oryginałów lub przepisywania wszystkiego od podstaw. (Co jest rzeczą, której nigdy nie powinieneś robić , jeśli możesz tego uniknąć.) Bądź ostrożny, rozumiesz swój podstawowy obiekt, bo możesz skończyć z zabójczymi kangurami .
Dla mnie są to podstawowe zasady programowania obiektowego: zarządzanie złożonością, centralizacja kodu i ulepszone modelowanie domen problemowych poprzez tworzenie klas obiektów, dziedziczenie i polimorfizm oraz zwiększone bezpieczeństwo bez poświęcania mocy lub kontroli poprzez zastosowanie enkapsulacji i nieruchomości. Mam nadzieję, że to pomoże ci zrozumieć, dlaczego tak wielu programistów uważa to za przydatne.
EDYCJA: W odpowiedzi na pytanie Joela w komentarzach,
Małe zastrzeżenie tutaj. Mój model „programu obiektowego” to w zasadzie model Delphi, który jest bardzo podobny do modelu C # / .NET, ponieważ zostały one utworzone przez byłych członków zespołu Delphi. To, co tu mówię, może nie mieć zastosowania lub nie dotyczyć tak dużo w innych językach OO.
Program zorientowany obiektowo to taki, w którym cała logika jest zorganizowana wokół obiektów. Oczywiście trzeba to gdzieś zabrać ze sobą. Twój typowy program Delphi zawiera kod inicjujący, który tworzy obiekt singleton o nazwie
Application
. Na początku programu wywołujeApplication.Initialize
, a następnie wezwanie doApplication.CreateForm
każdego formularza, który chcesz załadować do pamięci od początku, a następnieApplication.Run,
wyświetla główny formularz na ekranie i uruchamia pętlę wejścia / zdarzenia, która tworzy rdzeń dowolnego interaktywne programy komputerowe.Aplikacja i formularze sprawdzają przychodzące zdarzenia z systemu operacyjnego i tłumaczą je na wywołania metod na obiekcie. Jedną z rzeczy, która jest bardzo powszechna, jest użycie procedur obsługi zdarzeń lub „delegatów” w .NET-speak. Obiekt ma metodę, która mówi: „wykonaj X i Y, ale sprawdź również, czy jest przypisana ta konkretna procedura obsługi zdarzeń, i wywołaj ją, jeśli jest”. Moduł obsługi zdarzeń jest wskaźnikiem metody - bardzo prostym zamknięciem zawierającym odwołanie do metody i odwołanie do instancji obiektu - która służy do przedłużenia zachowania obiektów. Na przykład, jeśli w formularzu mam obiekt przycisku, dostosowuję jego zachowanie, dołączając moduł obsługi zdarzeń OnClick, który powoduje, że jakiś inny obiekt wykonuje metodę po kliknięciu przycisku.
Tak więc w programie zorientowanym obiektowo większość pracy wykonuje się poprzez zdefiniowanie obiektów z pewnymi obowiązkami i połączenie ich ze sobą, albo poprzez wskaźniki metod, albo przez jeden obiekt bezpośrednio wywołujący metodę zdefiniowaną w publicznym interfejsie innego obiektu. (A teraz wracamy do enkapsulacji.) Jest to pomysł, że nie miałem pojęcia powrotu, zanim zacząłem zajęcia OOP na studiach.
źródło
Myślę, że OOP to po prostu nazwa nadana czemuś, co mogłeś kusić po drodze, tak jak ja.
Dawno temu, kiedy byłem programistą dla dzieci, nawet w Fortranie, istniało coś takiego jak wskaźnik do podprogramu. Naprawdę przydatne jest przekazanie wskaźnika do podprogramu jako argumentu do innego podprogramu.
Następnie następną rzeczą, która byłaby naprawdę przydatna, byłoby przechowywanie wskaźnika do podprogramu w rekordzie struktury danych. W ten sposób można powiedzieć, że rekord „wie”, jak wykonywać operacje na sobie.
Nie jestem pewien, czy kiedykolwiek wbudowali to w Fortran, ale łatwo to zrobić w C i jego potomkach.
Poniżej znajduje się prosty i użyteczny pomysł, który mógłbyś skłonić do zrobienia samemu, i jest łatwiejszy do zrobienia w nowszych językach, nawet jeśli niektórzy ludzie zamienili go w gigantyczny modę pełną przerażających modnych słów.
źródło
Istnieją różne rodzaje systemów OO i trudno jest znaleźć definicję, na którą wszyscy się zgodzą. Zamiast próbować pokazać, jak OO Javy jest podobny do Common Lisp Object System, zacznę od czegoś bardziej konwencjonalnego, krok po kroku.
Załóżmy, że istnieje wiele obiektów w postaci rozproszonych danych. Punkty, na przykład, mogą być elementami w tablicy X, Y i Z. W celu rozpatrzenia samego punktu, ma sens, aby wyciągnąć wszystkie dane razem w coś jak C
struct
.Teraz dla każdego obiektu danych mamy dane razem. Jednak w programie proceduralnym kod jest rozproszony. Załóżmy, że mamy do czynienia z kształtami geometrycznymi. Istnieje duża funkcja do rysowania kształtów i musi ona wiedzieć o wszystkich kształtach. Istnieje duża funkcja znajdowania obszaru, a druga dla obwodu. Kod koła jest rozproszony przez wiele funkcji, a aby dodać inny typ kształtu, musimy wiedzieć, które funkcje zmienić. W systemie obiektowym zbieramy funkcje do tego samego rodzaju (
class
) danych. Dlatego jeśli chcemy spojrzeć na cały kod okręgu, jest on wCircle
definicji, a jeśli chcemy dodaćQuartercircle
, po prostu piszemy jego klasę i mamy kod.Korzyścią wynikającą z jednej strony jest to, że możemy utrzymywać niezmienniki klas, co jest prawdą o każdym członku klasy. Ograniczając kod spoza klasy do bezpośredniego komunikowania się z członkami danych klasy, mamy cały kod, który może zmieniać dane klasy w jednym miejscu, i możemy potwierdzić, że nie robi nic złego (na przykład trójkąta z jedną nogą dłuższe niż pozostałe dwa razem). Oznacza to, że możemy liczyć na niektóre właściwości każdego członka klasy i nie musimy sprawdzać, czy obiekt jest zdrowy przy każdym użyciu.
Główną korzyścią jest dziedziczenie i polimorfizm. Definiując wszystkie te kształty jako podklasy nazwanej klasy
Shape
, możemy zmusić nasz kod do manipulacjiShape
s, a zadaniem podobiektu kształtu jest zrobienie wszystkiego, co jest wymagane przez manipulacje. Oznacza to, że nie musimy dotykać starego przetestowanego kodu, gdy dodajemy nowe kształty lub udoskonalamy zachowanie starszych. Automatycznie mamy stary kod, który może bezpośrednio skorzystać z nowego kodu. Zamiast uświadamiania kodu sterującego wszystkich możliwych kształtów i konieczności utrzymywania funkcji, które są świadome wszystkich różnych możliwych kształtów, zajmujemy się jedynie kształtami i ich właściwościami, zachowującShape
podklasy. Upraszcza to kod kontrolny.Mamy tutaj kilka zalet. Ponieważ mamy niezmienniki klas, możemy wnioskować o większych obiektach danych w taki sam sposób, jak my o typach wbudowanych, co oznacza, że często możemy dzielić złożone koncepcje na prostsze. Ponieważ kod okręgu jest w dużej mierze zawarty
Circle
, zwiększyliśmy lokalizację. Ponieważ nie ma koncepcji koła rozproszonego przez kilka różnych funkcji w różnych miejscach, otrzymujemy mniej sprzężenia między procedurami i nie musimy martwić się o ich synchronizację. Ponieważ klasy są w rzeczywistości typami, możemy skorzystać z istniejącego systemu typów, aby złapać niekompatybilne użycie naszych klas.źródło
OO ma wiele różnych definicji, tak. Jestem pewien, że możesz znaleźć wiele z nich na własną rękę. Osobiście lubię Rees Re: OO jako sposób na ich zrozumienie. Myślę, że już to czytałeś, odkąd zacytowałeś Paula Grahama. (Polecam ją wszystkim zainteresowanym OO.) Zamierzam mniej więcej przyjąć definicję Java tutaj {1,2,3,7,8,9}.
Pytanie o użyteczność OO, szczególnie sposób, w jaki do niej podchodzę, zasługuje na znacznie większą odpowiedź z kilkoma tysiącami wierszy kodu (częściowo po to, aby nie być tylko garstką asercji). Oto jednak streszczenie tego hipotetycznego dokumentu.
Nie sądzę, że OO jest strasznie przydatne na małą skalę, powiedzmy, około kilkuset linii. W szczególności języki OO bez dobrych wpływów funkcjonalnych sprawiają, że bardzo proste jest wykonywanie prostych czynności przy użyciu dowolnego rodzaju kolekcji lub czegokolwiek, co wymaga wielu typów danych. To tutaj pojawia się większość wzorów; są pasmami wspomagającymi niską moc podstawowego języka .
Przy około tysiącu linii zaczyna być trudniej śledzić wszystkie operacje i struktury danych oraz ich powiązania. Pomaga w tym momencie w sposób jawny organizować struktury danych i operacje, rysować granice modułów i definiować obowiązki oraz mieć wygodny sposób na zrozumienie tych definicji, gdy próbujesz programować przeciwko nim.
Java-ish OO to rozwiązanie w połowie tych problemów, które wygrały w konkursie popularności. Ponieważ jest to ten sam mechanizm, który ludzie Javy stosują do drobnych problemów spowodowanych słabym językiem, zaczyna wyglądać bardziej jak magiczne rozwiązanie wszystkiego niż tylko sposób na utrzymanie porządku. Ludzie zaznajomieni z programowaniem funkcjonalnym preferują inne rozwiązania, takie jak CLOS lub klasy Haskella, lub metaprogramowanie szablonów, gdy utknąłem w C ++, albo też (jak ja, pracując codziennie w C #) używają OO, ale po prostu nie ekscytują się tym. .
źródło
OOP próbuje modelować koncepcje świata rzeczywistego pod względem obiektów i interakcji między nimi. Jako ludzie, mamy tendencję do przetwarzania świata w kategoriach przedmiotów. Świat jest pełen obiektów, które mają określone właściwości i mogą robić takie rzeczy, jak interakcja z innymi obiektami. OOP pozwala modelować świat na podobnych zasadach. Na przykład,
Ale samochód nie jest w stanie poruszać się sam, potrzebuje osoby do kierowania nim - interakcji między Przedmiotami.
źródło
OOP = struktury danych + przekazywanie wiadomości + dziedziczenie, z których wszystkie są logicznymi zmianami w modelach programowania.
OOP można zrozumieć (przez programistów) w około 90 sekund (link znajduje się w moim profilu). Pojęcia są bardzo proste.
Jak zastosować to inna sprawa. To, że wiesz, jak wymachiwać młotem, nie oznacza, że wiesz, jak zaprojektować i zbudować dom. ;-)
źródło
Jakiś czas temu napisałem post na blogu, który może Ci się przydać: Objaśnienie proceduralne a OOP .
źródło
Po raz pierwszy to zrozumiałem:
Przed programowaniem obiektowym miałeś programowanie strukturalne . Wszystko koncentruje się wokół procesu. Pierwsze pytanie, które musisz sobie zadać, brzmi: „ Co chcę zrobić z informacjami? ”.
Programowanie obiektowe koncentruje się wokół danych. Pierwsze pytanie, które musisz sobie zadać, brzmi: „ Informacje o czarownicach, z którymi muszę sobie poradzić? ”. To ułatwia abstrakcję.
źródło
Ponieważ rozumiesz struktury i rozumiesz wskaźniki funkcji i rozumiesz struktury za pomocą wskaźników funkcji, z twojej perspektywy zdefiniowałbym programowanie obiektowe jako po prostu „programowanie, z dużym wykorzystaniem struktur, które mają wskaźniki funkcji”. Nadal programuje w tradycyjnym znaczeniu - wszystkie dane i kod, który działa na dane. Różnica polega po prostu na tym, jak wszystkie te informacje są zdefiniowane i jak podejść do ich zdefiniowania.
Być może nadmiernym uproszczeniem jest to, że tradycyjne programowanie to „kod z pewnymi strukturami danych”, a programowanie obiektowe to „struktury danych z pewnym kodem”. Oba nadal mają struktury danych i oba nadal mają kod. Zatem programowanie obiektowe jest niczym innym jak tylko definiowaniem rodzajów danych z góry i egzekwowaniem umów dotyczących sposobu, w jaki komunikują się za pomocą zestawów funkcji.
Jak zauważyłeś, istnieje ogromna klasa aplikacji, dla których nie jest to świetny sposób na wdrożenie rozwiązania. Wygląda na to, że żyjesz w świecie złożonym głównie z takich aplikacji. W swoim wpisie na blogu wspominasz o implementacji problemu „99 butelek piwa” („ulubiona prezentacja programowa”). 99 butelek piwa z pewnością należy do tej kategorii. Próba zrozumienia programowania obiektowego poprzez spojrzenie na implementacje 99 butelek piwa przypomina trochę próbę zrozumienia architektury wieżowca poprzez spojrzenie na domek na drzewie. Nawet bardzo dobrze zbudowany domek na drzewie może cię tylko tyle nauczyć.
TL; DR: Programowanie OO jest podobne do programowania tradycyjnego, tyle że skupiasz więcej wysiłku na definiowaniu struktur danych z góry, a struktury danych komunikują się ze sobą za pośrednictwem wskaźników funkcji.
źródło
Myślę, że strona Wikipedii jest dobrym miejscem na zdobycie podstaw:
http://en.wikipedia.org/wiki/Object-oriented_programming
Zasadniczo chodzi o to, że programowanie proceduralne, które OOP próbował ulepszyć, koncentrowało się na modelowaniu procesów. OOP przechodzi na model, w którym nacisk kładziony jest na „rzeczy”, które modelujesz, a procesy i dane tych rzeczy są zawarte w tych rzeczach.
Na przykład załóżmy, że projektujesz aplikację do śledzenia listy zadań. W programowaniu proceduralnym jednostkami najwyższego poziomu w modelu byłyby zachodzące procesy, takie jak tworzenie zadania, usuwanie zadania, zmiana informacji o zadaniu itp. W modelu OOP zamiast tego skupiałbyś się na tworzeniu Zadania i zastanów się, za jakie dane i procesy powinien odpowiadać Zadanie. A następnie skup się na innych obiektach, z którymi Zadanie powinno wchodzić w interakcje, takich jak Notatka lub coś, jeśli chcesz zachować notatki o Zadaniach.
Mam nadzieję że to pomogło. Po prostu czytaj dalej i patrz na kod, a on nagle „kliknie”. To było moje doświadczenie.
źródło