0
votes

Right now I have a small application for calculating statistics with a parallel algorithms. Now I've got a problem with expanding some part of functionality. I'll try to explain shortly. App is build on revel framework. One of the actions of "stat" controller takes incoming POST json. Parses it. And generates two channels (goroutines) for tasks and results. All that works like a charm. But I have troubles with models. I wrote code to be able to expand models amount linearly, but for this moment there was only one in work.

And not all of methods are adopted for this expanding.

In some part of code I have this :

for t := range in {
        for sourceName, charts := range t.Request.Charts {

            var cacheData []byte
            var deserializedData models.StatModel

            //determine the model type
            switch sourceName {
            case "noagg":
                deserializedData = new(models.NoaggModel)
            case "acsi":
                deserializedData = new(models.AcsiModel)
            }

            cache_err := cache.Get(string(string(sourceName) + "_" + string(t.Date)), &cacheData);
            if cache_err != nil {
                panic("the cache is empty")
            }

            marshal_error := json.Unmarshal([]byte(cacheData), &deserializedData)
            if marshal_error == nil {

            }

            deserializedData.FilterData(t.Request.Filters)

            deserializedData.ClusterData(t.Request.Filters)

            w := Work{}
            for _, chart := range charts {
                countedData := ChartElements{}

                if marshal_error == nil {
                    countedData = deserializedData.CountDataForChart(string(chart.Name))
                }else {
                    panic("some is bad")
                }

                w.Name, w.Data = chart.Name, countedData
                out <- w
            }
        }
    }

Noagg model and Asci model are implementing the same interface of the "stat" model :

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

But now I have to add some new models with the same interface, but there is code, that I can't expand . I can't remember how to do this..

func statCount(model NoaggRow, f func(NoaggRow) float64) float64 {
    countedStat := f(model)
    return countedStat
}

func Count(model NoaggRow, name string) float64{

    m := map[string]func(NoaggRow) float64 {
        "HOLD" : HOLD,
        "INB"  : INB,
        "AHT"  : AHT,
        "RING" : RING,
        "TALK" : TALK,
        "ACW"  : ACW,
        "OCC"  : OCC,
    }
    countedStat := statCount(model, m[name])
    return countedStat
}

The same methods I need for ASCI model. For AcsiRow instead of NoaggRow. How to make this input params types dynamic or how to make methods common for all models. Only array and names of "map[string]func(......Row)" would differ in this place. Can anyone help me out with this ?

1

1 Answers

1
votes

If the functions in the map (that is, HOLD(), INB(), etc) do not need more access to the models than the StatModel interface provides, then your requirement should be possible by changing these functions to receive a StatModel instead of a NoaggRow:

func HOLD(s StatModel) float64 {
    ...
}

Then the AcsiRow functions that you want to add can have the same signature, and statCount and Count can be rewritten as follows:

func statCount(model StatModel, f func(StatModel) float64) float64 {
    countedStat := f(model)
    return countedStat
}

func Count(model StatModel, name string) float64 {
    m := map[string]func(StatModel) float64 {
        "HOLD" : HOLD,
        "INB"  : INB,
        "AHT"  : AHT,
        "RING" : RING,
        "TALK" : TALK,
        "ACW"  : ACW,
        "OCC"  : OCC,
        // Map AcsiModel functions here, let's call them ACSIn here
        // (you mentioned that the names would be different, so
        // I assume they don't get in the way of the functions
        // above):
        "ACSI1": ACSI1,
        "ACSI2": ACSI2,
        "ACSI3": ACSI3,
    }
    countedStat := statCount(model, m[name])
    return countedStat
}

Disclaimer: This is just an idea that is based on what I can see in the post. I know nothing about Revel and maybe I thus might have missed some important detail that is induced by the Revel context that would prevent implementing this approach.

Update based on the comment:

Use type assertions to enable the HOLD, OCC, etc. functions to access struct specific attributes, like so:

func HOLD (s StatModel) float64 {
    noagg, ok := s.(NoaggRow) // typecast from StateModel to NoaggRow
    if !ok {
        // Looks like the caller did not pass a matching model/name pair into Count().
        // Error handling...
    }
    attr := noagg.AttributeSpecificToNoaggRow
    ...
}

This might look somehow risky at runtime, but if the caller always passes matching pairs of model/name values to Count() (and I assume it does so), the typecast should be safe.