Czy wyjątki są koncepcją OOP?

37

Po przeczytaniu wczoraj postu zdałem sobie sprawę, że niewiele wiem o pochodzeniu wyjątków. Czy to tylko koncepcja związana z OOP? Myślę, że tak, ale znowu są wyjątki od bazy danych.

John V.
źródło
Kiedyś przeczytałem, że „Pan Goodenuf” (lub podobny) wynalazł wyjątki w odpowiedzi na „nieważne, to Goodenuf, eh!” szykanowanie stylu. Nie mogę teraz znaleźć odniesienia - może ktoś inny może. Interesujące byłoby wiedzieć, do którego języka dodano je w pierwszej kolejności.
Steve314,
7
Haskell ma wyjątki i wcale nie jest to OOP
Daniel Gratzer
1
Chociaż same wyjątki nie są ściśle zorientowane obiektowo, powszechna praktyka definiowania wyjątków jako obiektów oraz rzucania i łapania instancji tych obiektów wyraźnie jest bardzo OOP.
Dougvj
Jaka jest różnica między wyjątkiem a GOTO, byłoby ciekawym pytaniem.
Austin Henley,
2
@Austin - co powiesz na „chociaż wyjątki rzucają w życie zasadę pojedynczego punktu wyjścia, że ​​niektórzy najsurowsi uczniowie programowania strukturalnego opowiadają się za absolutem, nie pozwalają na nieograniczony przepływ kontroli spaghetti w ten sposób goto. W szczególności cel rzut zależy od kontekstu, opartego na zagnieżdżeniu struktur blokowych. Jako takie wyjątki są nawet zależne od nieco mniej ścisłej formy programowania strukturalnego, w której zasada pojedynczego wyjścia jest traktowana jako wytyczna, ale nie absolut ”.
Steve314,

Odpowiedzi:

5

Wyjątki nie są koncepcją OOP.

Ale nie są one całkowicie niezwiązane ani w jednym małym maleńkim punkcie.

Jak pokazały inne odpowiedzi: koncepcja wyjątków pojawiła się w kilku językach innych niż OOP. Nic w tej koncepcji nie wymaga czegoś od OOP.

Ale wszelkie, jeśli nie wszystkie języki OOP, które traktują OOP poważnie, wymagają wyjątków, ponieważ inne metody obsługi błędów zawodzą w jednym konkretnym punkcie: Konstruktor.

Jednym z punktów OOP jest to, że obiekt hermetyzuje i całkowicie zarządza swoim stanem wewnętrznym. Oznacza to również, że w czystym OOP potrzebujesz koncepcji stworzenia nowego obiektu ze spójnym stanem „atomowo” - wszystko, od alokacji pamięci (jeśli jest wymagana) do inicjalizacji do znaczącego stanu (tj. Proste zerowanie pamięci nie wystarczy) zrobić to w jednym wyrażeniu. Dlatego wymagany jest konstruktor :

Foo myFoo = Foo("foo", "bar", 42);

Ale to oznacza, że ​​konstruktor może również zawieść z powodu błędu. Jak propagować informacje o błędzie z konstruktora bez wyjątków?

  • Zwracana wartość? Zawodzi, ponieważ w niektórych językach newmoże zwrócić tylko, nullale nie ma żadnych istotnych informacji. W innych językach (np. C ++) myFoonie jest wskaźnikiem. Nie można tego sprawdzić null. Nie możesz też zapytać myFooo błąd - nie jest on inicjowany, a zatem „nie istnieje” w myśleniu OOP.

  • Globalna flaga błędu? Tyle o kapsułkowaniu stanu, a potem jakiejś zmiennej globalnej? Idź do h ... ;-)

  • Mikstura? W żadnym wypadku nie lepiej.

  • ?

Tak więc wyjątki są bardziej fundamentalną koncepcją niż OOP, ale OOP opiera się na nich w naturalny sposób.

AH
źródło
3
o ile wiem, jedynym słowem w tej „odpowiedzi”, która odnosi się do zadanego pytania, jest „nie”. Reszta zdaje się dotyczyć wyjątków w OOP - z całym szacunkiem dla mnie brzmi to gdzieś pomiędzy niejasno spokrewnionymi i całkowicie nieistotnymi - znowu, w kontekście
zadanego
@gnat: TO mówi również, że nie wie o początkach wyjątków. Stąd trochę tła, dlaczego wyjątki są wszędzie na ziemi OO, wydawało mi się w porządku. YMMV
AH
1
@AH Muszę się zgodzić z komarem, poza linią początkową, tak naprawdę to nie dotyczy pytania. Twoja odpowiedź na komara brzmiała: „mówi, że nie wie o pochodzeniu wyjątków”, ale tak naprawdę nie podałeś źródła wyjątków, tylko niektóre przypadkowe użycie wyjątków podczas tworzenia instancji obiektu.
Poważnie? -1? Większość innych odpowiedzi nie jest w 100% do rzeczy. +1 ode mnie, aby to zrekompensować. Ta odpowiedź daje dobre porady w tle w świecie wzorów zepsutych klas. (Poprawka: Należy unikać wzmianki o wieloetapowych konstruktorach)
Jo So
44

Czy dotyczy to tylko OOP?

Nie. Wyjątki i OOP nie są ze sobą powiązane.

Obsługa wyjątków jest mechanizmem obsługi błędów. Wyjątek jest obsługiwany przez zapisanie bieżącego stanu wykonania w predefiniowanym miejscu i przełączenie wykonania na określony podprogram znany jako moduł obsługi wyjątków.

Porównując C ( niezupełnie język OOP , możliwe w jakiś sposób emulować wyjątki w C ) i C ++ (OOP, obsługuje wyjątki), nic nie stoi na przeszkodzie, aby standardowa komisja C dodawała obsługę wyjątków do C, nadal nie uczyni z C języka OOP.

BЈовић
źródło
2
Można nawet argumentować, że istnieją już pewne wyjątki obsługiwane w zwykłym systemie operacyjnym. Pozwól, aby Twój program się zawiesił (nieprzechwycony „wyjątek”), a dzięki zrzutowi pamięci i debuggerowi możesz nawet uzyskać ślad stosu.
bhaak
12
Nawet MS BASIC z początku lat 80. miał obsługę wyjątków:ON ERROR GOTO xxxx
jwernerny
1
@bhaak Mógł także mówić o zrzutach pamięci w systemie Windows
JohnL
11
@jwernerny Obsługa błędów? Pewnie. Ale nikt nie nazwałby obsługi tego wyjątku. W rzeczywistości był rutynowo przeciwstawiany (ustrukturyzowanej) obsłudze wyjątków.
Konrad Rudolph
1
@ jwernerny nie jestem pewien, czy śledzę; obsługa wyjątków, jak rozumiem, jest to bardzo specyficzny sposób obsługi błędów. Kiedy słyszę wyjątek, zawsze myślę o try catchkonstrukcji.
Andy,
12

Wyjątkiem jest, mówiąc wprost, wyjątkowa sytuacja wymagająca uwagi i często zmiany w przebiegu wykonywania programu. Zgodnie z tą definicją wyjątki i obsługa wyjątków nie ograniczają się do orientacji obiektowej, a proste błędy programu można uznać za formę wyjątku.

Języki zorientowane obiektowo zazwyczaj mają natywną klasę wyjątków, a w zależności od kontekstu słowo „wyjątek” może rzeczywiście odnosić się do tej klasy natywnej zamiast ogólnej koncepcji. Obsługa wyjątków zorientowanych obiektowo jest, jak większość zorientowana obiektowo, cukrem syntaktycznym i może być łatwo emulowana w zdecydowanie nie zorientowanych obiektowo językach. Oto przykład C z wikibooka Programowanie C :

#include <stdio.h>
#include <setjmp.h>

jmp_buf test1;

void tryjump()
{
    longjmp(test1, 3);
}

int main (void)
{
    if (setjmp(test1)==0) {
        printf ("setjmp() returned 0.");
        tryjump();
    } else {
        printf ("setjmp returned from a longjmp function call.");
    }
}
Yannis
źródło
6
To nie jest tylko cukier składniowy. Odtworzenie pełnego odwijania stosu i obsługi przechwytywania opartych na typach jest trudne dzięki setjmp. Dodatkowo specjalizacja kompilacji wyjątków daje korzyści, których setjmp nie może naśladować.
edA-qa mort-ora-y
3
Nienawidzę opisu wyjątki są wyjątkowymi sytuacjami. Wolałbym raczej powiedzieć, że wyjątki powinny być generowane (zgłaszane), gdy nie można rozwiązać problemu z bieżącym kontekstem, ponieważ w bieżącym kontekście nie ma wystarczającej ilości informacji, aby poprawnie naprawić błąd.
Martin York,
+1 dla setjmp.h
gnat
9

Odpowiedź jest prosta NIE.

Dobrym przykładem języka innego niż OO z wyjątkami jest ADA.

Uwe Plonus
źródło
4
Hm, dlaczego ADA nie jest językiem OO? To prawda, że ​​ADA83 nie miał polimorfizmu, ale nadal można go uznać za oparty na obiektach. Również od ADA95 język jest w pełni zorientowany obiektowo.
yannis
O ile wiem, obsługa wyjątków jest starsza niż ADA83, dlatego sama ADA sama w sobie nie jest jednostką organizacyjną z obsługą wyjątków.
Uwe Plonus
2
@YannisRizos: Ada83 ma pakiety i pakiety ogólne, ale nie ma obiektów. Zostały wprowadzone wraz z Ada95.
mouviciel
2
@Yannis - Obiekty bez polimorfizmu są jak programowanie strukturalne bez zagnieżdżonych bloków. Polimorfizm jest jedną z charakterystycznych cech OOP. Nawet w Ada95 typy obsługujące wiązanie w czasie wykonywania są nazywane „typami otagowanymi”, a nie „klasami”, choć oczywiście to tylko pisownia. Ada 83 miała zapisy wariantów i różne inne typy, ale żaden z tych typów nie zapewnia funkcji specyficznych dla OOP. Ada 83 była modułowa i miała strukturę, ale nie była zorientowana obiektowo.
Steve314
3
@Yannis - w zasadzie, niektórzy ludzie ze społeczności Ada (jak niektórzy zwolennicy większości języków) nie mogą zaakceptować, że funkcja może być dobra, ale nie jest zaimplementowana w ich ulubionym języku, i wymyślą wymówki, by wierzyć inaczej. Jednak nawet nie jest tak, że dobry język musi mieć wszystkie możliwe dobre cechy językowe (choć łatwo w to uwierzyć projektantom Ady). Nie jestem zwolennikiem minimalistycznego podejścia do projektowania języków, ale języki maksymalistyczne też nie są idealne.
Steve314
7

Kilka bardzo dobrych odpowiedzi już tutaj. Inne przykłady języków programowania innych niż OOP z wyjątkami:

  • Oracle PL / SQL

  • klasyczny Visual Basic (wersja 6 i niższa, „On Error Goto” to IMHO forma obsługi wyjątków)

(Szczerze mówiąc: w niektórych językach można znaleźć elementy OO, ale chyba mechanika obsługi wyjątków ich nie używa, ponieważ koncepcja została wprowadzona wiele lat przed dodaniem elementów OO do tych języków).

Doktor Brown
źródło
Przynajmniej późniejsze wersje QuickBASIC na DOS (wcześniejsza niż Visual Basic; QB 4.5 to 1988 według Wikipedii, VB 1.0 1991) miały obsługę błędów przy użyciu ON ERROR GOTOskładni. Nawet QuickBASIC miał kilka koncepcji podobnych do OO (myślę, że QB 4.5 nawet wspierał jakieś klasy), ale trudno byłoby nazwać w większości tradycyjny BASIC właściwym językiem zorientowanym obiektowo. [Wikipedia ]
CVn
5

Podstawową ideą wyjątków jest oczyszczenie przepływu programu, aby programiści mogli łatwiej podążać „normalną” ścieżką wykonania. Rozważ prosty przypadek otwarcia pliku w C. Natychmiast po próbie otwarcia pliku programista musi zbadać odpowiedź wywołania fopen () i zdecydować, czy wywołanie się powiodło. Jeśli wywołanie nie powiedzie się, programista musi odpowiednio odpowiedzieć. Następne wywołanie w „normalnej” ścieżce wykonania, być może wywołanie fread () lub fwrite (), pojawi się po obsłużeniu warunków błędu lub niepowodzenia. To może być na następnym ekranie.

W języku, który zapewnia wyjątki, po równoważnym wywołaniu fopen () może natychmiast następować fread () lub fwrite (). Nie ma obsługi błędów, które ukrywają „następny krok” „normalnej” ścieżki wykonania. Programista może zobaczyć więcej normalnej ścieżki na jednym ekranie, dzięki czemu może łatwiej śledzić wykonanie. Obsługa błędów jest przenoszona do innej części programu.

Same wyjątki nie są koncepcją OOP, ale często są wdrażane przy użyciu koncepcji OOP, dzięki czemu są wygodniejsze i wydajniejsze. Na przykład wyjątki można zdefiniować za pomocą hierarchii dziedziczenia. Korzystając z naszego hipotetycznego przykładu otwierania, odczytu lub zapisu pliku, każde z tych wywołań może generować różne wyjątki - FileClosedException, DeviceFullException, NoSuchFileException, InsufficientFilePermissionsException itp. Każdy z nich może dziedziczyć po FileException, który może odziedziczyć po IOException, który może Dziedzicz z GenericException.

Jeśli programista wykonuje szybką i nieprzyzwoitą implementację w celu przetestowania koncepcji, może w większości zignorować obsługę wyjątków i po prostu zaimplementować pojedynczy moduł obsługi dla GenericException. Ten moduł obsługi będzie obsługiwał GenericException i każdy wyjątek, który dziedziczy z GenericException. Jeśli chce traktować dowolny wyjątek związany z plikiem w ten sam sposób, może napisać moduł obsługi wyjątku FileException. Będzie to wywoływane w przypadku wyjątków FileException i wszelkich wyjątków dziedziczących po FileException. Jeśli chce napisać program, który będzie różnie reagował na różne warunki błędu, może napisać określoną procedurę obsługi dla każdego konkretnego wyjątku.

MikeD
źródło
3

Inni słusznie odpowiedzieli „Nie” z przykładami języków. Pomyślałem, że mogę rozszerzyć, dodając przykład dodawania wyjątków do języka bez angażowania OOP.

Zrobię to w przypadku DSKL (deklaratywnego języka sekwencyjnego jądra) OZ , języka dobrze przystosowanego do tego rodzaju zajęć akademickich. DSKL (lub DKL) można zobaczyć tutaj (losowy wynik wyszukiwania), część Oświadczenia i wartości. Dokładna definicja nie jest ważna, poza tym, że jest to bardzo prosty język bez zmiennych, które można modyfikować (są zadeklarowane i później powiązane), i nie ma wbudowanego OOP.

Nie można nawet dodać OOP jako abstrakcji językowej do tego języka jądra. Dodając unikalne nazwy do języka jądra (NewName) i używając lokalnego zakresu, można uzyskać enkapsulację. Lub dodając zmienny stan do języka jądra (NewCell) i używając lokalnego zakresu można uzyskać właściwe OOP z enkapsulacją. Ale nie można tego osiągnąć tylko z określonym językiem jądra.

Jeśli następnie dodamy wyjątki do języka jądra, będziemy mieli język bez obsługi OOP, ale z wyjątkami. Pokażę jak:

Definiując maszynę abstrakcyjną ze stosem i pamięcią, możemy zdefiniować, co powinna zrobić każda instrukcja w naszym języku ( semantyka instrukcji). Na przykład skipw stosie nie należy nic robić, A = 3w stosie należy powiązać (/ ujednolicić) A z (/ with) 3.

Zaczynamy od dodania składni sposobu definiowania naszych wyjątków. Robimy to, dodając kolejne dwie klauzule do <statement>DKL.

<statement>  ::== ... (old stuff)
                 | try <statement> catch <id> then <statement> end
                 | raise <id> end

Oto znany try / catch i sposób na zgłaszanie wyjątków.

Ich semantykę definiujemy według tego, jak powinny działać na maszynie abstrakcyjnej:

Spróbuj
Oświadczenie semantyczne brzmi: (try <statement1> catch <id> then <statement2> end)
Wykonaj:

  1. Wciśnij na stos instrukcję semantyczną (catch <id> then <statement2> end)
  2. Wciśnij na stos instrukcję semantyczną (<statement1>)

Zauważ, że instrukcja 1 będzie na górze stosu i najpierw zostanie wykonana.

Raise
Stwierdzenie semantyczne brzmi: (raise <id> end)
Do:

  1. Jeśli na stosie nie ma nic więcej, zatrzymaj się i zgłoś nieprzechwycony wyjątek.
  2. W przeciwnym razie wstaw pierwszą instrukcję semantyczną ze stosu. Jeśli nie jest to instrukcja catch, przejdź do kroku 1.
  3. Mamy haczyk, w formie (catch <id> then <statement> end)
    Push (<statement>)na stosie.

Catch
Jeśli podczas normalnego wykonywania widzimy instrukcję catch, oznacza to, że wszystko, co było w środku, zostało wykonane bez podniesienia wyjątków do tego poziomu. W ten sposób po prostu otwieramy catchstos i nic nie robimy.

QED, mamy język z wyjątkami i brak możliwości OOP.

Usunąłem część środowiska z abstrakcyjnej maszyny, aby była prostsza.

Matsemann
źródło
1

Nie.

IIRC, wyjątki pojawiły się przed pierwszymi językami OO. AFAIK, wyjątki były najpierw wspierane przez wczesne wdrożenia LISP. Języki wczesnej struktury (np. ALGOL) i wczesne języki OO (np. SIMULA) nie obsługiwały wyjątków.

Kevin Cline
źródło
ALGON 68 oczywiście miał wyjątki („zdarzenia”), ale miał też wszystko inne. PL / Miałem je również („warunki ON”), a literatura z 1969 roku opisuje ich użycie.
Ross Patterson