Go:带接口的struct的动态类型转换/断言(调用方法和使用struct commons)

Alt*_*ion 7 reflection go type-assertion revel

我试图让我的代码变得更简洁,更清洁.问题是,在一个功能,那就是工作不同structs,那implements一个interface.

在某些情况下,我需要model变量来实现结构(rowModel的切片)([] rowModel),有时我需要使用接口中的方法.代码并不简短,对不起.所以我把主要评论放在下面的代码中.

这是界面:

type StatModel interface {
    FilterData(Filter)
    ClusterData(Filter)
    CountDataForChart(string)[]ChartElement
    GroupByTreeGroups(Filter)[]OrgPack
}

type StatRow interface {
    Count( name string) float64
}
Run Code Online (Sandbox Code Playgroud)

此接口是为方法调用创建的,并且可以缩短代码.但是接口不能在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
}
Run Code Online (Sandbox Code Playgroud)

所以,我正在阅读频道,获得不同的结构,并尝试正确计算所有内容.在这种情况下如何正确地进行类型断言和方法调用?

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
}
Run Code Online (Sandbox Code Playgroud)

非常感谢所有帮助.如有必要,我会回答有关此主题的每个问题.

更新1:

修改了一些动态生成结构的东西.现在所有人都在正确编译,直到我需要获取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
}
Run Code Online (Sandbox Code Playgroud)

Jes*_*ano 3

根据您跳过newItem函数中前六个字段的方式,这些属性似乎如下:

type BaseModel struct {
    Date             string
    Hour             int
    Id_user          int
    Id_line          float64
    Id_region        int
    Id_tree_devision int
}
Run Code Online (Sandbox Code Playgroud)

对所有型号都是通用的。为什么不嵌入这些值呢?


是否有某种原因您的OrgPack结构不能只保存一个nextIdLine int值或类似的东西?我认为这可能会比使用反射和切片长度来计算行 id 值产生更干净的代码。


如果你做了以上两件事,你也可以轻松地替换

func newItem(modelName string, el models.OrgPack) interface{}
Run Code Online (Sandbox Code Playgroud)

func (el OrgPack) NewNoagg() Noagg
func (el OrgPack) NewFcr() Fcr
Run Code Online (Sandbox Code Playgroud)

也许

type RowFactory interface { New(el OrgPack) StatRow }
type NoaggFactory struct{}
func (_ NoaggFactory) New(el OrgPack) StatRow
Run Code Online (Sandbox Code Playgroud)

在后一种情况下,您可以将RowFactory属性附加到OrgPacks,而不是 s,或者除了 s 之外ModelName,这将允许您生成正确的StatRow值,而无需切换字符串值。


正如您所指出的,切换的每种情况receiveLightWork本质上都是相同的:您创建一个新元素切片,以某种方式对它们进行“集群”,格式化输出,然后返回它。

切片的创建可以通过Factory类似的接口来完成,如上所述。ClusterData已经是一个接口方法了。FormatOutput也许应该是。

如果您将取决于您正在使用的数据类型的逻辑移动到这些类型的方法中,我认为应该可以实现如下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
    }
Run Code Online (Sandbox Code Playgroud)