vendredi 19 août 2016

Go: Dynamic type cast/assertion of struct's with interface (to call methods and use struct commons)

I cracked my brain trying to make my code shorter and cleaner. The problem is in one function, that is working with different structs, that implements one interface.

In some cases I need the model variable to implement the structure (slice of rowModel's) ([]rowModel) and some times I need to use methods from interface. The code is not short, sorry for that. So I put main comments in the code below.

Here is interface:

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

type StatRow interface {
    Count( name string) float64
}

This interfaces are created for methods calls, and to make code shorter. But Interface cannot have fields or structure as Abstruct class in OOP. One of the models is here:

 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
}

So , I'm reading from channel, and getting different structures, and trying to calculate everything correctly. How to make type assertion and method calls correctly in this case?

func receiveLightWork(org <-chan models.OrgPack, request ChartOptions) interface{} {

    modelClusters := make(map[string][]models.OrgPack)

    for orgPack := range org {
        _, ok := modelClusters[orgPack.ModelName]
        if ok {
            model := modelClusters[orgPack.ModelName]
            model = append(model, orgPack)
            modelClusters[orgPack.ModelName] = model

        }else {
            var modelSlice []models.OrgPack
            modelSlice = append(modelSlice, orgPack)
            modelClusters[orgPack.ModelName] = modelSlice
        }
    }

    output := make(map[string][]OrgStat)

    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)
        }

        customFilter := request.Filters
        customFilter.Cluster = "clusterDay"

        model.ClusterData(customFilter) // now here is unresolved Reference 'ClusterData'

        for _, mod := range model {
            for _, charts := range request.Charts {
                    var stats []OrgStat
                    for _, chart := range charts {

                        // now here is unresolved Reference 'Count'
                        stats = append(stats, OrgStat{Name:chart.Name, Value: mod.Count(mod, chart.Name)}) 
                    }

                    _, group_exist := output[mod.Date]
                    if group_exist {
                        inserted_stat := output[mod.Date]
                        output[mod.Date] = append(stats, inserted_stat...)
                    }else {
                        output[mod.Date] = stats                        }

                }
        }
    }

    return output
}

All help is very highly appreciated. I'll answer to each question on this topic if necessary. Thanks in advance!





Aucun commentaire:

Enregistrer un commentaire