Miksi Go:ssa ei ole Try-Catch -rakennetta?
Go ei tue tarkoituksellisesti try-catch -rakennetta, vaan ainoastaan panic-recover-syntaksia. Tämä aiheuttaa tyytymättömyyttä lukuisissa kehittäjissä, jotka ovat tottuneet käyttämään try-catchiä virheiden käsittelyssä. Miksi sitten try-catchiä ei ole lisätty? Syynä on se, että try-catchillä on useita ongelmakohtia.
try-catch-finally
Try-catch-rakenne on tarkoitettu käsittelemään virhetilanteita ja poikkeustilanteita, joita voi esiintyä ohjelman suorituksen aikana (ajonaikaisesti). Lisäksi finally-rakenne sisältää koodin, joka tulee suorittaa ehdoitta riippumatta siitä, onko poikkeusta tapahtunut vai ei.
Virheiden käsittely ja vastuu
1980- ja 1990-luvuilla virheiden käsittely oli hyvin yksinkertaista. Virheilmoitukset olivat vain 'levyke täynnä', 'levykeasemaa ei ole', 'levykkeelle ei ole kirjoitusoikeutta', ja tuon ajan kehittäjät heittivät virheet virheenkäsittelypisteeseen saakka, missä ne käsiteltiin keskitetysti. Tällaisessa tilanteessa try-catch-rakenne toimi tehokkaasti.
Tilanne on kuitenkin muuttunut ajan myötä. Liiketoimintalogiikat ovat monimutkaistuneet, tietokannat ja transaktiot ovat tulleet mukaan, ja lukemattomia API:ja kutsutaan verkon kautta, jolloin lukemattomia pyyntöviestejä täytyy analysoida. Jopa samanaikainen ohjelmointi on tuonut mukanaan tarpeen käsitellä virheitä muissa säikeissä kuin pääsäikeessä.
Virheiden käsittelystä on tullut niin monimutkaista, ettei sitä voida enää hoitaa yhdessä paikassa, eikä kukaan pysty olemaan vastuussa kaikista virheistä. Tässä vaiheessa try-catchin vakavat ongelmat alkavat nousta esiin.
Try-catchin vastuun siirtäminen
Try-catch, yksinkertaistettuna, on tapa, jolla virheen aiheuttaja siirtää vastuun (jälkikäsittelyn) virheen syntymisestä jollekin toiselle. Tämä toinen voi olla catch-rakenne, oma yliluokkansa, yliluokan yliluokka, tai jopa yliluokan yliluokan yliluokka... Joka tapauksessa, maailmassa, jossa virheiden käsittely lisääntyy ja monimutkaistuu, try-catchin valitsema strategia on 'kyllä joku hoitaa tämän'. Tarkastellaan alla olevaa koodia.
1try {
2 data = readFile("hello.txt");
3 structuredData = parseData(data);
4 insertDBStatus(structuredData[1]);
5 startHTTPServer(structuredData[2]);
6} catch (Exception e) {
7 e.printStackTrace();
8}
Yllä olevan koodin ongelma on, että ei ole selvää, kuka on vastuussa printStackTrace-käsittelystä, eikä myöskään tiedetä, missä koodissa virhe on tapahtunut. Lisäksi, mitä enemmän logiikkaa on try-lauseessa, sitä pahemmaksi ongelma muuttuu. Paradoksaalisesti, mitä monimutkaisemmaksi kehitystyö muuttuu, sitä riippuvaisemmiksi kehittäjät ovat tulleet try-catch-rakenteesta, joka siirtää vastuuta. He eivät enää mieti virheiden käsittelyä, vastuuntunto on heikentynyt, ja lopulta he unohtavat virheiden ja poikkeusten käsittelyn perimmäisen tarkoituksen. Miten golang on sitten ratkaissut tämän ongelman?
panic, recover
Yksi go:n vahvuuksista on sen järjestelmät, jotka ohjaavat kehittäjiä oikealle tielle, eivätkä anna heidän ajautua huonoihin käytäntöihin. Panic-recover on myös yksi tällainen esimerkki.
Panic ja recover vaikuttavat ensisilmäyksellä samalta kuin try-catch, mutta ero piilee siinä, ettei vastuu siirry ulkopuolelle, kun ongelma ilmenee. Kun go:ssa tapahtuu panic, arvoa ei siirretä ulkopuolelle, vaan se tulee ratkaista siinä kohdassa, jossa panic tapahtui. Tämä antaa kehittäjille vastuun virheiden käsittelystä ja ohjaa heitä pohtimaan syvällisemmin, missä, kuka ja miten virheet tulee käsitellä. Käyttäjille taataan autonomia, mutta samalla annetaan tilaa ajattelulle.
Myös sanan 'panic' valinta on erinomainen, sillä toisin kuin try-recover, panic-recover jo pelkällä sanavalinnallaan painostaa kehittäjiä käyttämään sitä vain selkeissä virhetilanteissa, ei poikkeustilanteissa. Luonnollisesti kehittäjät eivät käytä recoveria väärin, vaan vain tarpeellisissa kohdissa. Tämä auttaa go:ssa kirjoittamaan ytimekästä koodia.