如何在golang中实现包模型的通用功能?

wkyowqbh  于 2022-12-20  发布在  Go
关注(0)|答案(1)|浏览(134)

我有2包作为模型:
类别:

package class

import (
    "encoding/json"
    "student_management/model/base"
)

type Classes struct {
    Classes []Class
}

type Class struct {
    Id                int `json:"student_id"`
    Name              int `json:"name"`
    HomeroomTeacherId int `json:"homeroom_teacher_id"`
}

func ReadData() (chan Class, int) {
    var classes Classes
    byteValue := base.ReadJSON("db/student_class.json")
    json.Unmarshal(byteValue, &classes)

    classChannel := make(chan Class)
    go func () {
        for i := 0; i < len(classes.Classes); i++ {
            classChannel <- classes.Classes[i]
        }
        close(classChannel)
    }()

    return classChannel, len(classes.Classes)
}

老师:

package teacher

import (
    "encoding/json"
    base "student_management/model/base"
)

type Teachers struct {
    Teachers []Teacher `json:"teachers"`
}

type Teacher struct {
    base.Persions
    HomeroomTeacher bool `json:"homeroom_teacher"`
}

func ReadData() (chan Teacher, int) {
    var teachers Teachers
    byteValue := base.ReadJSON("db/teachers.json")
    json.Unmarshal(byteValue, &teachers)

    teacherChannel := make(chan Teacher)
    go func () {
        for i := 0; i < len(teachers.Teachers); i++ {
            teacherChannel <- teachers.Teachers[i]
        }
        close(teacherChannel)
    }()

    return teacherChannel, len(teachers.Teachers)
}

你可以看到ReadData函数被重复了,现在我可以使用class.ReadData()teacher.ReadData()从通道调用数据。
我如何编写ReadData()函数一次,供两个包使用?
我尝试使用泛型创建一个基本包,如下所示:

package base

func ReadData[Models any, Model any](fileName string, m Models) (chan interface{}, int) {
    byteValue := ReadJSON(fileName)
    json.Unmarshal(byteValue, &m)

    channel := make(chan Model)
    go func () {
        for i := 0; i < len(m.Models); i++ {
            channel <- m.Models[i]
        }
        close(channel)
    }()

    return channel, len(models.Models)
}

但是找不到m.Models,我的意思是teachers.Teachersclasses.Classes不能使用
请告诉我在这种情况下该怎么办

xkrw2x1b

xkrw2x1b1#

使用泛型(在Go语言1.18中引入),创建一个ReadData()函数,使用一个参数类型来表示要从JSON解码并在通道上传递的值。
注意:您应该检查错误并报告它们(包括来自base.ReadJSON()的错误)。

func ReadData[T any](fileName, fieldName string) (chan T, int, error) {
    var m map[string][]T
    byteValue := base.ReadJSON(fileName)
    if err := json.Unmarshal(byteValue, &wrapper); err != nil {
        return nil, 0, err
    }

    values := m[fieldName]

    valuesChannel := make(chan T)
    go func() {
        for _, v := range values {
            valuesChannel <- v
        }
        close(valuesChannel)
    }()

    return valuesChannel, len(values), nil
}

调用它的示例:

ch, n, err := ReadData[class.Class]("db/student_class.json", "classes")

// or

ch, n, err := ReadData[teacher.Teacher]("db/teachers.json", "teachers")

注意,返回读取值的数量应该是多余的,因为你正确地关闭了返回的通道,调用者可以在返回的通道上使用for range,它将正确地接收所有发送到它上面的值,然后终止。
还要注意,由于返回通道时所有值都已就绪(解码),这种并发性是多余的,只会降低效率。您有一个解码值切片,只需返回它,让调用者选择希望如何处理它(并发或不并发)。
因此,您的ReadData()应该如下所示:

func ReadData[T any](fileName, fieldName string) ([]T, error) {
    var m map[string][]T
    byteValue := base.ReadJSON(fileName)
    if err := json.Unmarshal(byteValue, &wrapper); err != nil {
        return nil, err
    }

    return m[fieldName]
}

还要注意,如果输入JSON对象只有一个字段,则不必传递fieldName,可以从解码的mMap中获取值,如下所示:

func ReadData[T any](fileName string) ([]T, error) {
    var m map[string][]T
    byteValue := base.ReadJSON(fileName)
    if err := json.Unmarshal(byteValue, &wrapper); err != nil {
        return nil, err
    }

    for _, v := range m {
        return v, nil
    }

    return nil, errors.New("empty JSON")
}

然后简单地称之为:

classes, err := ReadData[class.Class]("db/student_class.json")

// or

teachers, err := ReadData[teacher.Teacher]("db/teachers.json")

相关问题