我不知所措,试图使代码更短,更清晰。问题出在一个函数中,即使用不同的structs
,即implements
一个interface
。
在某些情况下,我需要model
变量来实现结构(rowModel的 slice )([] rowModel),有时我需要使用接口(interface)中的方法。
代码不短,对此感到抱歉。因此,我将主要注释放在下面的代码中。
这是界面:
type StatModel interface {
FilterData(Filter)
ClusterData(Filter)
CountDataForChart(string)[]ChartElement
GroupByTreeGroups(Filter)[]OrgPack
}
type StatRow interface {
Count( name string) float64
}
该接口(interface)是为方法调用而创建的,并使代码更短。但是Interface不能将字段或结构作为OOP中的Abstruct类。其中一种模型在这里:
type NoaggModel []NoaggRow
type NoaggRow struct {
Date string
Hour int
Id_user int
Id_line float64
Id_region int
Id_tree_devision int
N_inb float64
N_out float64
N_hold float64
N_abandon float64
N_transfer float64
T_inb float64
T_out float64
T_hold float64
T_ring float64
T_acw float64
T_wait float64
}
type FcrModel []FcrRow
type FcrRow struct {
Date string
Hour int
Id_user int
Id_line float64
Id_region int
Id_tree_devision int
N_irr float64
N_inb float64
}
因此,我正在从通道中读取信息,并获得不同的结构,并尝试正确计算所有内容。在这种情况下,如何正确进行类型断言和方法调用?
func receiveLightWork(org <-chan models.OrgPack, request ChartOptions) interface{} {
modelClusters := make(map[string][]models.OrgPack)
// here I fill data into modelClusters
output := make(map[string][]OrgStat)
// here I begin loop over clusters of different model types
for modelName, slice := range modelClusters {
//here I can't choose what to write
// model must be convertable to NoaggModel, that is []NoaggRow{}
// as others AcsiModel, FcrModel ...etc.
// Also model.ClusterData(customFilter) must be callable as it is in interface of common model
var model []interface{}
var rowModel interface{}
switch modelName {
case "noagg":
model = model.(models.NoaggModel)
rowModel = rowModel.(models.NoaggRow{})
case "acsi":
model = model.(models.AcsiModel)
rowModel = rowModel.(models.AcsiRow)
case "fcr24":
model = model.(models.FcrModel)
rowModel = rowModel.(models.FcrRow)
case "aic":
model = model.(models.AicModel)
rowModel = rowModel.(models.AicRow)
}
for _, el := range slice {
modelFields := reflect.ValueOf(&rowModel).Elem()
sliceFields := reflect.ValueOf(&el.SummorisedData).Elem()
fieldsTypes := modelFields.Type()
for i := 6; i < modelFields.NumField(); i++ {
fmt.Println(" model_field ", fieldsTypes.Field(i).Name )
modelField := modelFields.Field(i);
sliceField := sliceFields.Index(i-6) ;
modelField.Set(reflect.Value(sliceField));
}
id_line := sliceFields.Index(len(el.SummorisedData) - 1) ;
date := sliceFields.FieldByName("PackName");
modelFields.FieldByName("Id_line").Set(id_line)
modelFields.FieldByName("Date").Set(date)
// here append not works, because model is []interface{} and not []NoaggRow or others.
// Writes [non-interface type []interface {} on left]
model = append(model, rowModel)
}
// here I need to call interface method for model
model.ClusterData(customFilter) // now here is unresolved Reference 'ClusterData'
for _, mod := range model {
// here some common logick for creating data for chart output
}
}
return output
}
非常感谢所有帮助。如有必要,我将回答有关此主题的每个问题。
更新1:
修改了一些用于动态生成struct的东西。现在一切都正确编译,直到需要获取struct实例的地方。它仅显示界面。注释和代码更新在这里:
func typeSwitch(model string) (interface{}, interface{}){
switch model{
case "noagg":
fmt.Println("Model type:", model)
return &models.NoaggModel{}, &models.NoaggRow{}
case "acsi":
fmt.Println("Model type:", model)
return &models.AcsiModel{}, &models.AcsiRow{}
case "fcr24":
fmt.Println("Model type:", model)
return &models.FcrModel{}, &models.FcrRow{}
case "aic":
fmt.Println("Model type:", model)
return &models.AicModel{}, &models.AicRow{}
default:
fmt.Println("Unknown")
return false,false
}
}
func receiveLightWork(org <-chan models.OrgPack, request ChartOptions) interface{} {
modelClusters := make(map[string][]models.OrgPack)
for orgPack := range org {
// here I fill data into clusters
}
output := make(map[string][]OrgStat)
// here I need common code to put data from clusters in correct structures and call interface methods
for modelName, slice := range modelClusters {
model, rowModel := typeSwitch(modelName)
var data_slice []interface{}
for _, el := range slice {
modelFields := reflect.ValueOf(rowModel).Elem()
fieldsCounter := modelFields.NumField()
sliceFields := reflect.ValueOf(&el.SummorisedData).Elem()
sliceObjFields := reflect.ValueOf(&el).Elem()
fieldsTypes := modelFields.Type()
for i := 6; i < fieldsCounter; i++ {
fmt.Println(" model_field ", fieldsTypes.Field(i).Name )
modelField := modelFields.Field(i);
sliceField := sliceFields.Index(i-6) ;
modelField.Set(reflect.Value(sliceField));
}
id_line := sliceFields.Index(len(el.SummorisedData) - 1) ;
date := sliceObjFields.FieldByName("PackName");
modelFields.FieldByName("Id_line").Set(id_line)
modelFields.FieldByName("Date").Set(date)
fmt.Println("row_data : ", rowModel)
data_slice = append(data_slice, rowModel)
}
// here comes : invalid type assertion: data_slice.(model) (non-interface type []interface {} on left
dataModel := data_slice.(model)
// here I need correctly created instance of model
// (NoaggModel or FcrModel) with data inside its struct
// to work with it and call interface methods that are shown in interface above
}
return output
}
最佳答案
根据您如何跳过newItem
函数的前六个字段,这些属性似乎如下:
type BaseModel struct {
Date string
Hour int
Id_user int
Id_line float64
Id_region int
Id_tree_devision int
}
所有型号都通用。为什么不embed这些值?
您的
OrgPack
结构不能仅仅保留nextIdLine int
值或类似内容吗?我认为这可能比使用反射和 slice 长度来计算行ID值的代码更简洁。如果您完成了上述两项操作,则可以轻松替换
func newItem(modelName string, el models.OrgPack) interface{}
与
func (el OrgPack) NewNoagg() Noagg
func (el OrgPack) NewFcr() Fcr
也许
type RowFactory interface { New(el OrgPack) StatRow }
type NoaggFactory struct{}
func (_ NoaggFactory) New(el OrgPack) StatRow
在后一种情况下,您可以将
RowFactory
属性附加到OrgPack
而不是ModelName
或作为其附加,这将使您无需切换字符串值即可生成正确的StatRow
值。正如您已经指出的,
receiveLightWork
中开关的每种情况基本上都是相同的:您创建了一片新元素,以某种方式“聚集”它们,格式化输出并返回它。如上所述,可以通过类似于
Factory
的接口(interface)来完成 slice 的创建。 ClusterData
已经是接口(interface)方法。 FormatOutput
可能应该是。如果将依赖于要处理的数据类型的逻辑转移到这些类型的方法中,我认为应该可以实现看起来像like this的
receiveLightWork
: func receiveLightWork(org <-chan models.OrgPack, request ChartOptions) map[string][]OrgStat {
modelClusters := make(map[string][]models.OrgPack)
for orgPack := range org {
if model, ok := modelClusters[orgPack.ModelName]; ok {
modelClusters[orgPack.ModelName] = append(model, orgPack)
} else {
modelClusters[orgPack.ModelName] = []models.OrgPack{orgPack}
}
}
customFilter := request.Filters
customFilter.Cluster = "clusterDay"
output := make(map[string][]OrgStat)
for modelName, slice := range modelClusters {
if len(slice) == 0 {
continue
}
model := slice[0].ModelFactory.New()
for _, el := range slice {
model.Add(el.RowFactory.New(el))
}
model.ClusterData(customFilter)
for sourceName, charts := range request.Charts {
output = model.FormatOutput(output, sourceName, charts)
}
}
return output
}