dsbfbz75185 2016-08-19 16:53
浏览 106
已采纳

转到:具有接口的结构的动态类型强制转换/断言(以调用方法并使用结构公用)

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)

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

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

Update 1:

Have modified few things for generating struct's on the fly. Now all is compiling correctly until the place, where I need to get instance of struct. It sees only interface.. The comments and code update is here:

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
}
  • 写回答

4条回答 默认 最新

  • dsk49208 2016-08-25 18:06
    关注

    Based on how you're skipping over the first six fields in your newItem function, it seems like these properties:

    type BaseModel struct {
        Date             string
        Hour             int
        Id_user          int
        Id_line          float64
        Id_region        int
        Id_tree_devision int
    }
    

    are common to all models. Why not embed these values?


    Is there some reason your OrgPack struct can't just hold a nextIdLine int value or something along those lines? I think that might result in cleaner code than using reflection and slice lengths to compute row id values.


    If you did the above two things, you could easily also replace

    func newItem(modelName string, el models.OrgPack) interface{}
    

    with

    func (el OrgPack) NewNoagg() Noagg
    func (el OrgPack) NewFcr() Fcr
    

    or perhaps

    type RowFactory interface { New(el OrgPack) StatRow }
    type NoaggFactory struct{}
    func (_ NoaggFactory) New(el OrgPack) StatRow
    

    In the latter case, you could attach RowFactory properties to your OrgPacks instead of, or in addition to, ModelNames, which would allow you to produce the correct StatRow values without needing to switch over string values.


    As you've noted, every case of your switch in receiveLightWork is essentially the same: you create a slice of new elements, "cluster" them somehow, format the output, and return it.

    The creation of the slice can be done through a Factory-like interface, as described above. ClusterData is already an interface method. FormatOutput probably should be.

    If you move logic that depends on the type of data you're working with into methods for those types, I think it should be possible to achieve a receiveLightWork that looks like this:

        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
        }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

悬赏问题

  • ¥20 idea运行测试代码报错问题
  • ¥15 网络监控:网络故障告警通知
  • ¥15 django项目运行报编码错误
  • ¥15 请问这个是什么意思?
  • ¥15 STM32驱动继电器
  • ¥15 Windows server update services
  • ¥15 关于#c语言#的问题:我现在在做一个墨水屏设计,2.9英寸的小屏怎么换4.2英寸大屏
  • ¥15 模糊pid与pid仿真结果几乎一样
  • ¥15 java的GUI的运用
  • ¥15 我想付费需要AKM公司DSP开发资料及相关开发。