Go Best Practices - การจัดการข้อผิดพลาด

นี่เป็นบทความแรกในชุดบทเรียนที่ฉันได้เรียนรู้ในช่วงสองสามปีที่ฉันได้ทำงานกับ Go in Production เรากำลังดำเนินการบริการ Go จำนวนมากในการผลิตที่ Saltside Technologies (psst ฉันจ้างหลายตำแหน่งใน Bangalore for Saltside) และฉันยังดำเนินธุรกิจของตัวเองด้วยซึ่ง Go เป็นส่วนสำคัญ

เราจะครอบคลุมวิชาที่หลากหลายทั้งขนาดเล็กและใหญ่

วิชาแรกที่ฉันต้องการกล่าวถึงในซีรี่ส์นี้คือการจัดการข้อผิดพลาด มันมักจะทำให้เกิดความสับสนและความรำคาญสำหรับนักพัฒนาซอฟต์แวร์ Go ใหม่

พื้นหลังบางส่วน - อินเทอร์เฟซข้อผิดพลาด

เพียงแค่เราอยู่ในหน้าเดียวกัน ในขณะที่คุณอาจรู้ว่าข้อผิดพลาดใน Go เป็นอะไรก็ได้ที่ใช้งานอินเทอร์เฟซข้อผิดพลาด นี่คือความหมายของอินเตอร์เฟสที่ดูเหมือนว่า:

อินเตอร์เฟสประเภทข้อผิดพลาด {
    ข้อผิดพลาด () สตริง
}

ดังนั้นทุกสิ่งที่ใช้เมธอดสตริง Error () สามารถใช้เป็นข้อผิดพลาดได้

ตรวจสอบข้อผิดพลาด

การใช้โครงสร้างข้อผิดพลาดและการตรวจสอบประเภท

เมื่อฉันเริ่มเขียน Go ฉันมักจะทำการเปรียบเทียบสตริงของข้อความแสดงข้อผิดพลาดเพื่อดูว่าเกิดข้อผิดพลาดประเภทใด (ใช่อายที่คิด แต่บางครั้งคุณต้องมองย้อนกลับไปข้างหน้า)

วิธีที่ดีกว่าคือการใช้ประเภทข้อผิดพลาด ดังนั้นคุณสามารถสร้างโครงสร้างที่ใช้อินเทอร์เฟซข้อผิดพลาดจากนั้นทำการเปรียบเทียบชนิดในคำสั่งสวิตช์

นี่คือตัวอย่างการใช้งานข้อผิดพลาด

พิมพ์ ErrZeroDivision struct {
    สตริงข้อความ
}
func NewErrZeroDivision (สตริงข้อความ) * ErrZeroDivision {
    ผลตอบแทน & ErrZeroDivision {
        ข้อความ: ข้อความ
    }
}
ข้อผิดพลาด func (e * ErrZeroDivision) ข้อผิดพลาด ()
    ส่งคืน e.message
}

ตอนนี้ข้อผิดพลาดนี้สามารถใช้ได้เช่นนี้

func main () {
    ผลข้อผิดพลาด: = หาร (1.0, 0.0)
    if err! = ไม่มี
        สวิตช์ผิดพลาด (ประเภท) {
        กรณี * ErrZeroDivision:
            fmt.Println (err.Error ())
        ค่าเริ่มต้น:
            fmt.Println ("เกิดอะไรขึ้น h * เพิ่งเกิดขึ้น?")
        }
    }
    fmt.Println (ผล)
}
func หาร (a, b float64) (float64, ข้อผิดพลาด) {
    ถ้า b == 0.0 {
        ส่งคืน 0.0, NewErrZeroDivision ("ไม่สามารถหารด้วยศูนย์")
    }
    ส่งคืน a / b, ไม่มี
}

นี่คือลิงก์ Go Play สำหรับตัวอย่างเต็มรูปแบบ สังเกตเห็นรูปแบบสวิตช์ err. (type) ซึ่งทำให้สามารถตรวจสอบประเภทข้อผิดพลาดที่แตกต่างกันได้มากกว่าสิ่งอื่น (เช่นการเปรียบเทียบสตริงหรือสิ่งที่คล้ายกัน)

การใช้แพ็กเกจข้อผิดพลาดและการเปรียบเทียบโดยตรง

วิธีการข้างต้นสามารถจัดการได้โดยใช้แพ็คเกจข้อผิดพลาด วิธีการนี้แนะนำให้ใช้สำหรับการตรวจสอบข้อผิดพลาดภายในแพ็คเกจที่คุณต้องการแสดงข้อผิดพลาดอย่างรวดเร็ว

var errNotFound = ข้อผิดพลาดใหม่ ("ไม่พบรายการ")
func main () {
    ข้อผิดพลาด: = getItem (123) // นี่จะทำให้เกิด errNotFound
    if err! = ไม่มี
        สวิตช์ผิดพลาด {
        กรณี errNotFound:
            log.Println ("ไม่พบรายการที่ขอ")
        ค่าเริ่มต้น:
            log.Println ("เกิดข้อผิดพลาดที่ไม่รู้จัก")
        }
    }
}

วิธีนี้ไม่ดีเมื่อคุณต้องการวัตถุข้อผิดพลาดที่ซับซ้อนมากขึ้นเช่น รหัสข้อผิดพลาด ฯลฯ ในกรณีนั้นคุณควรสร้างประเภทของคุณเองที่ใช้ส่วนต่อประสานข้อผิดพลาด

การจัดการข้อผิดพลาดทันที

บางครั้งฉันพบรหัสเช่นด้านล่าง (แต่มักมีปุยมากกว่า .. ):

ข้อผิดพลาด func example1 ()
    ข้อผิดพลาด: = call1 ()
    กลับมาผิดปกติ
}

ประเด็นตรงนี้คือความผิดพลาดนั้นไม่ได้ถูกจัดการทันที นี่เป็นวิธีการที่บอบบางเนื่องจากบางคนสามารถใส่รหัสระหว่าง err: = call1 () และ err ส่งคืนซึ่งจะทำลายเจตนาเนื่องจากอาจทำให้เกิดข้อผิดพลาดแรกได้ สองแนวทางอื่น:

// ยุบการส่งคืนและข้อผิดพลาด
ข้อผิดพลาด func example2 ()
    รับสาย 1 ()
}
// จัดการข้อผิดพลาดอย่างชัดเจนทันทีหลังจากการโทร
ข้อผิดพลาด func example3 ()
    ข้อผิดพลาด: = call1 ()
    if err! = ไม่มี
        กลับมาผิดปกติ
    }
    กลับศูนย์
}

วิธีการทั้งสองข้างต้นใช้ได้กับฉัน พวกเขาบรรลุสิ่งเดียวกันซึ่งก็คือ; ถ้ามีคนต้องการเพิ่มบางสิ่งหลังจาก call1 () พวกเขาจำเป็นต้องจัดการกับข้อผิดพลาด

นั่นคือทั้งหมดสำหรับวันนี้

คอยติดตามบทความถัดไปเกี่ยวกับ Go Best Practices ไปแข็งแรง :)

func main () {
    err: = readArticle ("Go Best Practices - การจัดการข้อผิดพลาด")
    if err! = ไม่มี
        ปิง ( "@ sebdah")
    }
}