Go / Interface дэвшилтэт хэрэглээ

Interface дэвшилтэт хэрэглээ

Өмнөх хичээлд interface-ийн үндсийг судалсан. Одоо interface-ийг илүү нарийн ашиглах аргуудыг сурна. Эдгээр техникүүдийг мэдсэнээр та Go-ийн хамгийн хүчирхэг хэлбэрийн нэгийг бүрэн эзэмшинэ.

Interface нэгтгэх (Embedding)

Go-д нэг interface-ийг нөгөөдөө оруулж болдог. Үүнийг interface embedding гэнэ.

go
package main

import "fmt"

type Reader interface {
    Read() string
}

type Writer interface {
    Write(text string)
}

// Reader ба Writer-ийг нэгтгэсэн шинэ interface
type ReadWriter interface {
    Reader
    Writer
}

type File struct {
    content string
}

func (f *File) Read() string {
    return f.content
}

func (f *File) Write(text string) {
    f.content = text
}

func process(rw ReadWriter) {
    rw.Write("Сайн уу, Go!")
    fmt.Println(rw.Read())
}

func main() {
    f := &File{}
    process(f)
}

File struct нь Read() болон Write() хоёуланг хэрэгжүүлсэн тул ReadWriter interface-д нийцдэг.

Type Assertion

Interface-ийн цаана ямар concrete тип байгааг шалгахдаа type assertion ашигладаг.

go
package main

import "fmt"

type Animal interface {
    Sound() string
}

type Dog struct{ Name string }
type Cat struct{ Name string }

func (d Dog) Sound() string { return "Хав хав" }
func (c Cat) Sound() string { return "Миаа" }

func describe(a Animal) {
    // Хоёр утга буцаадаг хэлбэр — аюулгүй
    if dog, ok := a.(Dog); ok {
        fmt.Printf("Энэ нохой: %s\n", dog.Name)
        return
    }
    if cat, ok := a.(Cat); ok {
        fmt.Printf("Энэ муур: %s\n", cat.Name)
        return
    }
    fmt.Println("Тодорхойгүй амьтан")
}

func main() {
    animals := []Animal{
        Dog{Name: "Бурхан"},
        Cat{Name: "Цагаан"},
    }
    for _, a := range animals {
        describe(a)
    }
}

a.(Dog) гэдэг нь "a нь Dog мөн үү?" гэж асуудаг. ok нь true байвал таарсан гэсэн үг.

Type Switch

Олон тип шалгах үед type switch хэлбэр хамаагүй цэвэр харагддаг:

go
package main

import "fmt"

func typeCheck(v interface{}) {
    switch val := v.(type) {
    case int:
        fmt.Printf("Бүхэл тоо: %d\n", val)
    case string:
        fmt.Printf("Мөр: %s\n", val)
    case bool:
        fmt.Printf("Логик утга: %v\n", val)
    case []int:
        fmt.Printf("int slice, урт: %d\n", len(val))
    default:
        fmt.Printf("Тодорхойгүй тип: %T\n", val)
    }
}

func main() {
    typeCheck(42)
    typeCheck("Монгол")
    typeCheck(true)
    typeCheck([]int{1, 2, 3})
    typeCheck(3.14)
}

v.(type) бичиглэл нь зөвхөн switch дотор ажилладаг онцлог байдал юм.

Empty Interface — any

Go-д interface{} буюу Go 1.18-с хойш any гэдэг нь ямар ч утга хүлээн авдаг:

go
package main

import "fmt"

func printAnything(values ...any) {
    for _, v := range values {
        fmt.Printf("Утга: %v, Тип: %T\n", v, v)
    }
}

func main() {
    printAnything(1, "хоёр", true, 3.14, []string{"a", "b"})
}

any нь маш уян хатан боловч хэт их ашиглавал код ойлгоход хэцүү болдог. Шаардлагагүй тохиолдолд конкрет тип ашиглах нь дээр.

Interface хэрэгжүүлэлт шалгах

Compile хийх үед interface-ийг зөв хэрэгжүүлсэн эсэхийг шалгах нэг арга:

go
package main

type Stringer interface {
    String() string
}

type Person struct {
    Name string
}

func (p Person) String() string {
    return p.Name
}

// Compile-ийн үед шалгалт — энэ мөр ажиллахгүй код боловч алдаа барьдаг
var _ Stringer = Person{}

func main() {}

var _ Stringer = Person{} гэдэг нь "Person нь Stringer-ийг хэрэгжүүлж байгаа уу?" гэж compile хийх үед шалгадаг. Хэрэгжүүлээгүй бол алдаа гарна.

Дараагийн хичээлд:

Go-д алдааг хэрхэн зохицуулах вэ — error interface болон алдаа буцаах хэв маягийг сурна. Энэ нь Go хэлний хамгийн өвөрмөц онцлогуудын нэг юм.