Βέλτιστες πρακτικές - Αντιμετώπιση σφαλμάτων

Αυτό είναι το πρώτο άρθρο σε μια σειρά από μαθήματα που έχω μάθει για τα δυο χρόνια που έχω εργαστεί με την Go στην παραγωγή. Έχουμε ένα καλό αριθμό Go υπηρεσιών στην παραγωγή στο Saltside Technologies (psst, προσλαμβάνω για πολλαπλές θέσεις στη Μπανγκαλόρ για το Saltside) και επίσης τρέχω τη δική μου επιχείρηση όπου Go είναι αναπόσπαστο μέρος.

Θα καλύψουμε ένα ευρύ φάσμα θεμάτων, μεγάλων και μικρών.

Το πρώτο θέμα που θα ήθελα να καλύψω σε αυτή τη σειρά είναι ο χειρισμός σφαλμάτων. Συχνά προκαλεί σύγχυση και ενόχληση για τους νέους προγραμματιστές Go.

Κάποιο υπόβαθρο - Η διεπαφή σφάλματος

Ακριβώς έτσι είμαστε στην ίδια σελίδα. Όπως ίσως γνωρίζετε ένα σφάλμα στο Go είναι απλά οτιδήποτε υλοποιεί τη διεπαφή σφάλματος. Αυτός είναι ο ορισμός της διεπαφής που μοιάζει με:

Τύπος διεπαφής σφάλματος {
    Σφάλμα () συμβολοσειρά
}}

Επομένως, οτιδήποτε εφαρμόζει τη μέθοδο συμβολοσειράς Error () μπορεί να χρησιμοποιηθεί ως σφάλμα.

Έλεγχος για σφάλματα

Χρήση σφαλμάτων και έλεγχος τύπων

Όταν άρχισα να γράφω Go, έκανα πολλές φορές συγκρίσεις των μηνυμάτων σφάλματος για να δούμε τι ήταν ο τύπος σφάλματος (ναι, ντροπή να σκεφτείς αλλά μερικές φορές πρέπει να κοιτάς ξανά για να προχωρήσεις).

Μια καλύτερη προσέγγιση είναι να χρησιμοποιηθούν τύποι σφαλμάτων. Έτσι μπορείτε (φυσικά) να δημιουργήσετε διαρθρώσεις που υλοποιεί τη διεπαφή σφάλματος και στη συνέχεια να κάνετε σύγκριση τύπου σε μια εντολή διακόπτη.

Ακολουθεί ένα παράδειγμα εφαρμογής σφάλματος.

τύπου ErrZeroDivision struct {
    Σειρά μηνύματος
}}
func NewErrZeroDivision (συμβολοσειρά μηνύματος) * ErrZeroDivision {
    επιστροφή & ErrZeroDivision {
        μήνυμα: μήνυμα,
    }}
}}
function (e * ErrZeroDivision) Σφάλμα () συμβολοσειράς {
    επιστροφή e.message
}}

Τώρα αυτό το σφάλμα μπορεί να χρησιμοποιηθεί έτσι.

func main () {
    αποτέλεσμα, err: = διαίρεση (1.0, 0.0)
    αν err! = μηδέν {
        σφάλμα διακοπής (τύπου) {
        περίπτωση * ErrZeroDivision:
            fmt.Println (err.Error ())
        Προκαθορισμένο:
            fmt.Println ("Τι συνέβη ακριβώς;")
        }}
    }}
    fmt.Println (αποτέλεσμα)
}}
delta (a, b float64) (float64, σφάλμα) {
    εάν b == 0,0 {
        επιστροφή 0.0, NewErrZeroDivision ("Δεν μπορεί να χωριστεί με μηδέν")
    }}
    επιστροφή a / b, μηδέν
}}

Ακολουθεί ο σύνδεσμος Go Play για το πλήρες παράδειγμα. Παρατηρήστε το μοτίβο σφάλματος διακόπτη (τύπου), το οποίο επιτρέπει τον έλεγχο για διαφορετικούς τύπους σφαλμάτων αντί για κάτι άλλο (όπως σύγκριση συμβολοσειρών ή κάτι παρόμοιο).

Χρησιμοποιώντας το πακέτο σφαλμάτων και την άμεση σύγκριση

Η παραπάνω προσέγγιση μπορεί εναλλακτικά να αντιμετωπιστεί χρησιμοποιώντας το πακέτο σφαλμάτων. Αυτή η προσέγγιση συνιστάται για ελέγχους σφάλματος στο πακέτο όπου χρειάζεστε μια γρήγορη αναπαράσταση σφάλματος.

var errNotFound = errors.New ("Το αντικείμενο δεν βρέθηκε")
func main () {
    err: = getItem (123) // Αυτό θα ρίξει errNotFound
    αν err! = μηδέν {
        σφάλμα διακόπτη {
        περίπτωση errNotFound:
            log.Println ("Το στοιχείο που ζητήθηκε δεν βρέθηκε")
        Προκαθορισμένο:
            log.Println ("Παρουσιάστηκε άγνωστο σφάλμα")
        }}
    }}
}}

Αυτή η προσέγγιση είναι λιγότερο καλή όταν χρειάζεστε πιο σύνθετα αντικείμενα σφάλματος π.χ. κωδικούς σφάλματος κλπ. Σε αυτή την περίπτωση θα πρέπει να δημιουργήσετε τον δικό σας τύπο που υλοποιεί τη διεπαφή σφάλματος.

Άμεση διαχείριση σφαλμάτων

Μερικές φορές έχω τον κωδικό όπως παρακάτω (αλλά συνήθως με περισσότερο χνούδι γύρω από ..):

function example1 () error {
    err: = κλήση1 ()
    σφάλμα επιστροφής
}}

Το σημείο εδώ είναι ότι το λάθος δεν χειρίζεται αμέσως. Αυτή είναι μια εύθραυστη προσέγγιση αφού κάποιος μπορεί να εισάγει κώδικα μεταξύ του err: = call1 () και του λάθους επιστροφής, που θα έσπαζε την πρόθεση, καθώς μπορεί να σκιάσει το πρώτο σφάλμα. Δύο εναλλακτικές προσεγγίσεις:

// Συρρίκνωση της επιστροφής και του σφάλματος.
function example2 () error {
    επιστροφή κλήσης1 ()
}}
// Κάνετε ρητό χειρισμό σφάλματος αμέσως μετά την κλήση.
function example3 () error {
    err: = κλήση1 ()
    αν err! = μηδέν {
        σφάλμα επιστροφής
    }}
    επιστρέψτε στο μηδέν
}}

Και οι δύο προσεγγίσεις είναι καλές μαζί μου. Επιτυγχάνουν το ίδιο πράγμα, το οποίο είναι. αν κάποιος πρέπει να προσθέσει κάτι μετά την κλήση1 () πρέπει να φροντίσει για το χειρισμό σφαλμάτων.

Αυτά για σήμερα

Μείνετε συντονισμένοι για το επόμενο άρθρο σχετικά με τις καλύτερες πρακτικές Go. Πηγαίνετε δυνατά :).

func main () {
    err: = readArticle ("Βέλτιστες πρακτικές - Αντιμετώπιση σφαλμάτων")
    αν err! = μηδέν {
        ping ("@ sebdah")
    }}
}}