Pojęcie pojedynczego wejścia, pojedynczego wyjścia (SESE) pochodzi z języków z jawnym zarządzaniem zasobami , takich jak C i asembler. W języku C taki kod spowoduje wyciek zasobów:
void f()
{
resource res = acquire_resource(); // think malloc()
if( f1(res) )
return; // leaks res
f2(res);
release_resource(res); // think free()
}
W takich językach masz zasadniczo trzy opcje:
Replikuj kod czyszczenia.
Ugh. Redundancja jest zawsze zła.
Użyj a, goto
aby przejść do kodu czyszczenia.
Wymaga to, aby kod czyszczenia był ostatnią rzeczą w funkcji. (I dlatego niektórzy twierdzą, że goto
ma swoje miejsce. I rzeczywiście - w C.)
Wprowadź zmienną lokalną i manipuluj przez nią przepływem sterowania.
Wadą jest to, że przepływ sterowania manipulowane przez składni (myślę break
, return
, if
, while
) jest o wiele łatwiejsze do naśladowania niż przepływ sterowania manipulowane przez stan zmiennych (bo te nie mają zmienne stanu, jeśli spojrzeć na algorytmie).
W asemblerze jest jeszcze dziwniej, ponieważ możesz wywołać dowolny adres w funkcji, gdy wywołujesz tę funkcję, co oznacza, że masz prawie nieograniczoną liczbę punktów wejścia do dowolnej funkcji. (Czasami jest to pomocne. Takie zespoły są powszechną techniką dla kompilatorów do implementacji this
dostosowania wskaźnika koniecznego do wywoływania virtual
funkcji w scenariuszach wielokrotnego dziedziczenia w C ++.)
Gdy trzeba ręcznie zarządzać zasobami, korzystanie z opcji wprowadzania lub wychodzenia z funkcji w dowolnym miejscu prowadzi do bardziej złożonego kodu, a tym samym do błędów. Dlatego pojawiła się szkoła myślenia, która propagowała SESE, aby uzyskać czystszy kod i mniej błędów.
Jednak, gdy język zawiera wyjątki, (prawie) dowolna funkcja może zostać przedwcześnie zakończona (prawie) w dowolnym momencie, więc i tak należy przewidzieć możliwość przedwczesnego powrotu. (Myślę, że finally
jest głównie używany do tego w Javie i using
(podczas implementacji IDisposable
, w finally
przeciwnym razie) w C #; C ++ zamiast tego wykorzystuje RAII .) Po wykonaniu tej czynności nie można nie posprzątać po sobie z powodu wczesnego return
oświadczenia, więc co jest prawdopodobnie najsilniejszy argument przemawiający za SESE zniknął.
To pozostawia czytelność. Oczywiście funkcja 200 LoC z return
losowo posypanymi pół tuzinem instrukcji nie jest dobrym stylem programowania i nie zapewnia czytelnego kodu. Ale taka funkcja nie byłaby łatwa do zrozumienia bez tych przedwczesnych zwrotów.
W językach, w których zasoby nie są lub nie powinny być zarządzane ręcznie, przestrzeganie starej konwencji SESE ma niewielką lub żadną wartość. OTOH, jak argumentowałem powyżej, SESE często czyni kod bardziej złożonym . To dinozaur, który (oprócz C) nie pasuje dobrze do większości współczesnych języków. Zamiast pomagać w zrozumieniu kodu, utrudnia go.
Dlaczego programiści Java trzymają się tego? Nie wiem, ale z mojego (zewnętrznego) POV Java wzięła wiele konwencji z C (gdzie mają sens) i zastosowała je do swojego świata OO (gdzie są bezużyteczne lub wręcz złe), gdzie teraz się trzyma im, bez względu na koszty. (Podobna konwencja do definiowania wszystkich zmiennych na początku zakresu).
Programiści trzymają się różnego rodzaju dziwnych zapisów z irracjonalnych powodów. (Głęboko zagnieżdżone instrukcje strukturalne - „groty strzał” - były w takich językach jak Pascal, kiedyś postrzegane jako piękny kod.) Zastosowanie czystego logicznego rozumowania wydaje się nie przekonać większości z nich do odstąpienia od ustalonych sposobów. Najlepszym sposobem na zmianę takich nawyków jest prawdopodobnie nauczenie ich wcześnie, jak robić to, co najlepsze, a nie to, co konwencjonalne. Jako nauczyciel programowania masz go w ręku.:)