goto wcale nie jest takie złe

Chyba każdy z nas na początku nauki programowania usłyszał „najważniejszą zasadę pisania dobrego kodu”: nigdy nie używaj instrukcji goto. Uczenie młodych programistów, że unikanie goto to najważniejsza rzecz podczas programowania niesie ze sobą jedną dość poważną konsekwencję.

Chodzi o powszechne używanie dużo gorszych rozwiązań.

Zdarza się, że musimy napisać kod, który wykonuje się nieliniowo. Najprostszym przykładem jest zwykłe rozgałęzienie w postaci instrukcji if oraz else, ale czasem są to bardziej złożone konstrukcje, w których mamy kilka możliwych ścieżek a wykonanie każdej z nich jest zależne od kombinacji różnych warunków.

Programiści często w takim przypadku wprowadzają flagi – zmienne logiczne, których wartość zależy od tego co wydarzyło się wcześniej, a wpływa na wybór ścieżki według której program się wykonuje. Z tego co obserwuję taka sytuacja ma miejsce wtedy kiedy fragment kodu jest modyfikowany wielokrotnie, przez różnych programistów na przestrzeni dłuższego czasu, a każdy z nich dodaje kolejny fragment: wyrażenie warunkowe, zmienną stanową, nie dokonując żadnego refaktoringu tego co zastał.

Takie flagi mają zwykle do siebie to, że są widoczne w bardzo szerokim zakresie, np. całej, dość długiej funkcji lub klasie, i służą do tego aby w zależności od wyniku w jednym miejscu, wykonał się taki lub inny kod z innego zwykle odległego miejsca.

Są więc ukrytym sposobem na wymuszenie bardzo nieliniowego i trudnego w śledzeniu przepływu programu. Mało tego! Często, z konieczności, są mutowalne, tzn. mogą zmieniać swoją wartość, przez co analiza kodu jest bardzo ciężka bo wymaga sprawdzenia kto i kiedy może na ich stan wpłynąć.

Uważam, że w takim przypadku instrukcja goto byłaby lepszym rozwiązaniem. Przede wszystkim jest ona widocznym sposobem na wymuszenie nieliniowego przepływu programu. Po drugie często jej użycie doprowadziłoby do nieco mniej złożonego kodu: nie wiem co Wy o tym myślicie, ale mi najtrudniej się rozszyfrowuje te fragmenty kodu, które są zależny od mutowalnego stanu.

Czy instrukcja goto jest więc dobra? Nie. Kod pisany z jej użyciem jest zawsze trudny do śledzenia. Uważam jednak, że jest prostszy niż taki, którego przepływ jest zależny od flag. A co najważniejsze – jest dużo prostszy do refaktoryzacji do właściwych konstrukcji: dekoratorów, strategii itp.

Podsumowując…

Jeśli piszecie kod i widzicie, że przy pewnych warunkach wymagany jest skok do oddalonego fragmentu, po prostu go zróbcie instrukcją goto. Sprawdźcie potem czy testy przechodzą, tzn. czy kod robi to co ma robić i – jeśli tak – przystąpcie do refaktoringu.

Ale niech nie polega on na zastąpieniu goto, flagami i dodatkowymi instrukcjami if. To nie byłby krok do przodu – raczej w bok, albo wręcz do tyłu. Zastąpcie goto właściwym rozwiązaniem, np. wzorcem stragetia.