英文:
How to common function of package model in golang?
问题
你可以尝试使用接口来解决这个问题。首先,定义一个接口,该接口包含ReadData
函数所需的方法。然后,让Classes
和Teachers
类型实现该接口。这样,你就可以在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 (
"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)
}
teacher:
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)
}
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, &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)
}
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, &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
}
Example calling it:
ch, n, err := ReadData[class.Class]("db/student_class.json", "classes")
// or
ch, n, err := ReadData[teacher.Teacher]("db/teachers.json", "teachers")
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, &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, &wrapper); err != nil {
return nil, err
}
for _, v := range m {
return v, nil
}
return nil, errors.New("empty JSON")
}
And then calling it is simply:
classes, err := ReadData[class.Class]("db/student_class.json")
// or
teachers, err := ReadData[teacher.Teacher]("db/teachers.json")
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论