如何在Golang中使用包模块的常用函数?

huangapple go评论83阅读模式
英文:

How to common function of package model in golang?

问题

你可以尝试使用接口来解决这个问题。首先,定义一个接口,该接口包含ReadData函数所需的方法。然后,让ClassesTeachers类型实现该接口。这样,你就可以在base包中编写一个通用的ReadData函数,该函数接受实现了该接口的类型作为参数。以下是示例代码:

package model

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

type DataReader interface {
	ReadData() (chan interface{}, int)
}

type Classes struct {
	Classes []Class
}

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

func (c Classes) ReadData() (chan interface{}, int) {
	byteValue := base.ReadJSON("db/student_class.json")
	json.Unmarshal(byteValue, &c)

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

	return classChannel, len(c.Classes)
}

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

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

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

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

	return teacherChannel, len(t.Teachers)
}

然后,在base包中编写通用的ReadData函数,该函数接受实现了DataReader接口的类型作为参数:

package base

import (
	"encoding/json"
)

func ReadData(d DataReader) (chan interface{}, int) {
	return d.ReadData()
}

现在,你可以在其他地方调用base.ReadData函数,并传入实现了DataReader接口的类型作为参数,以实现代码的重用性。例如:

package main

import (
	"fmt"
	"student_management/model"
	"student_management/model/base"
)

func main() {
	classChannel, classCount := base.ReadData(model.Classes{})
	teacherChannel, teacherCount := base.ReadData(model.Teachers{})

	// 使用classChannel和teacherChannel进行后续操作
	// ...

	fmt.Println(classCount, teacherCount)
}

希望这可以帮助到你!

英文:

I have 2 package as model:

class:

package class
import (
&quot;encoding/json&quot;
&quot;student_management/model/base&quot;
)
type Classes struct {
Classes []Class
}
type Class struct {
Id                int `json:&quot;student_id&quot;`
Name              int `json:&quot;name&quot;`
HomeroomTeacherId int `json:&quot;homeroom_teacher_id&quot;`
}
func ReadData() (chan Class, int) {
var classes Classes
byteValue := base.ReadJSON(&quot;db/student_class.json&quot;)
json.Unmarshal(byteValue, &amp;classes)
classChannel := make(chan Class)
go func () {
for i := 0; i &lt; len(classes.Classes); i++ {
classChannel &lt;- classes.Classes[i]
}
close(classChannel)
}()
return classChannel, len(classes.Classes)
}

teacher:

package teacher
import (
&quot;encoding/json&quot;
base &quot;student_management/model/base&quot;
)
type Teachers struct {
Teachers []Teacher `json:&quot;teachers&quot;`
}
type Teacher struct {
base.Persions
HomeroomTeacher bool `json:&quot;homeroom_teacher&quot;`
}
func ReadData() (chan Teacher, int) {
var teachers Teachers
byteValue := base.ReadJSON(&quot;db/teachers.json&quot;)
json.Unmarshal(byteValue, &amp;teachers)
teacherChannel := make(chan Teacher)
go func () {
for i := 0; i &lt; len(teachers.Teachers); i++ {
teacherChannel &lt;- teachers.Teachers[i]
}
close(teacherChannel)
}()
return teacherChannel, len(teachers.Teachers)
}

So you can see the ReadData function being repeated. And now I can use class.ReadData() and teacher.ReadData() to call data from channel.

How can I write ReadData() function once for both packages to use?

I tried creating a base package use generics like this:

package base
func ReadData[Models any, Model any](fileName string, m Models) (chan interface{}, int) {
byteValue := ReadJSON(fileName)
json.Unmarshal(byteValue, &amp;m)
channel := make(chan Model)
go func () {
for i := 0; i &lt; len(m.Models); i++ {
channel &lt;- m.Models[i]
}
close(channel)
}()
return channel, len(models.Models)
}

but m.Models not found, i mean teachers.Teachers or classes.Classes can not be used

Please tell me what to do in this case

答案1

得分: 1

使用泛型(在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, &m); 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")

// 或者

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, &m); err != nil {
        return nil, err
    }

    return m[fieldName], nil
}

还要注意,如果输入的JSON对象只有一个字段,就不需要传递fieldName,你可以像这样从解码的m映射中获取值:

func ReadData[T any](fileName string) ([]T, error) {
    var m map[string][]T
    byteValue := base.ReadJSON(fileName)
    if err := json.Unmarshal(byteValue, &m); 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")

// 或者

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

Use generics (introduced in Go 1.18). Create a single ReadData() function, use a parameter type denoting the values you want to decode from JSON and deliver on the channel.

Note: you should check for errors and report them (including from 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, &amp;wrapper); err != nil {
return nil, 0, err
}
values := m[fieldName]
valuesChannel := make(chan T)
go func() {
for _, v := range values {
valuesChannel &lt;- v
}
close(valuesChannel)
}()
return valuesChannel, len(values), nil
}

Example calling it:

ch, n, err := ReadData[class.Class](&quot;db/student_class.json&quot;, &quot;classes&quot;)
// or
ch, n, err := ReadData[teacher.Teacher](&quot;db/teachers.json&quot;, &quot;teachers&quot;)

Note that it should be redundant to return the number of read values. Since you properly close the returned channel, the caller can use a for range over the returned channel which will receive all values sent on it properly, and then terminate.

Also note that since all values are ready (decoded) when you return the channel, this concurrency is redundant and just makes things less efficient. You have a slice of the decoded values, just return it and let the caller choose how it wishes to process it (concurrently or not).

So your ReadData() should look like this:

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

Also note that if the input JSON object has a single field, it's not necessary to pass the fieldName, you can get the value from the decoded m map like this:

func ReadData[T any](fileName string) ([]T, error) {
var m map[string][]T
byteValue := base.ReadJSON(fileName)
if err := json.Unmarshal(byteValue, &amp;wrapper); err != nil {
return nil, err
}
for _, v := range m {
return v, nil
}
return nil, errors.New(&quot;empty JSON&quot;)
}

And then calling it is simply:

classes, err := ReadData[class.Class](&quot;db/student_class.json&quot;)
// or
teachers, err := ReadData[teacher.Teacher](&quot;db/teachers.json&quot;)

huangapple
  • 本文由 发表于 2022年12月18日 18:21:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/74840569.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定