【问题标题】:How can I use an interface to normalize different structs?如何使用接口来规范化不同的结构?
【发布时间】:2020-02-02 21:25:02
【问题描述】:

我正在使用 Go 来收集各种 perfmon 统计数据,并希望将它们标准化为类似 E-A-V 模型的东西,但我正在努力思考如何实现这一目标。

鉴于这些示例结构:

type Win32_PerfFormattedData_Counters_ProcessorInformation struct {
    Name                    string
    C1TransitionsPersec     uint64
    C2TransitionsPersec     uint64
    C3TransitionsPersec     uint64
    PercentUserTime         uint64
    PercentInterruptTime    uint64
    PercentPrivilegedTime   uint64
    PercentC1Time           uint64
    PercentC2Time           uint64
    PercentC3Time           uint64
}
type Win32_PerfFormattedData_Tcpip_NetworkAdapter struct {
    Name                        string
    BytesSentPersec             uint64
    BytesReceivedPersec         uint64
    OffloadedConnections        uint64
    PacketsOutboundDiscarded    uint64
    PacketsOutboundErrors       uint64
    PacketsReceivedDiscarded    uint64
    PacketsReceivedErrors       uint64  
}

我想将它们标准化为:

type Counter struct {
    counter_category        string
    counter_name            string
    counter_instance        string
    counter_value           uint64
}

使用reflect我已经能够想出这个:

func pivot(cpu_info *[]Counter, cpu Win32_PerfFormattedData_Counters_ProcessorInformation, category string) {
    e := reflect.ValueOf(&cpu).Ellem()
    for i := 0; i < e.NumField(); i++ {
        f_name := e.Type().Field(i).Name
        f_value := e.Field(i).Interface()
        if f_name != "Name" {
            c := Counter {
                counter_category: category,
                counter_name: f_name,
                counter_instance: cpu.Name,
                counter_value: f_value.(uint64),
            }
            *cpu_info = append(*cpu_info,c)
        }
    }
}

但是,我正在收集 17 个计数器类别,我不认为编写其中的 17 个函数是最好的主意(除非它是并且我很高兴得到纠正)。

我已经用一个使用接口的函数走了这么远,但我只是不知道我是否走在正确的道路上:

func pivot_counter(counter interface{}, counter_info *[]Counter) {
    var counter_category string
    switch t := counter.(type) {
    case []Win32_PerfFormattedData_Counters_ProcessorInformation:
        counter_category = "Processor"
        fmt.Printf("%T\r\n",t)
    case []Win32_PerfRawData_Tcpip_NetworkAdapter:
        counter_category = "Network Adapter"
        fmt.Printf("%T\r\n",t)
    default:
        counter_category = "Unknown"
        fmt.Printf("%T\r\n", t)
    }
}

但这就是我卡住的地方,因为我仍然需要在这些案例中创建 17 个不同的案例语句,其中包含 17 个不同的“枢轴”逻辑块。我知道我做错了什么。我实际上是在尝试编写一个函数,该函数将接受结构/接口,通过类型断言确定类型,然后使用该对象迭代结构字段以将它们转换为同质格式。我该从我目前所在的地方去哪里?

【问题讨论】:

    标签: go struct interface


    【解决方案1】:

    定义你的界面:

    type CounterMaker interface {
        MakeCounter() Counter
    }
    

    现在,对于您拥有的每种类型,使其支持 CounterMaker:

    type Win32_PerfFormattedData_Counters_ProcessorInformation struct {
        ...
    }
    
    func (value Win32_PerfFormattedData_Counters_ProcessorInformation) MakeCounter() Counter {
        ... code to turn "value" into a counter ...
        return result
    }
    

    对其他类型重复。

    (上面显示的界面不一定是正确的,请选择适合您情况的界面。)

    现在,如果您有一些需要 Counter 实例的函数,您可以在调用它之前或在它内部创建一个。例如:

    func Increment(running *Counter, newstuff CounterMaker) {
        inc := CounterMaker.MakeCounter()
        // maybe double check that the running counter matches w/ "inc"
        running.counter_value += inc.counter_value
    }
    

    如果您使用此示例运行(同样,这可能不是正确的方法)您现在可以将Win32_PerfFormattedData_Counters_ProcessorInformation 值作为第二个参数传递给Increment,因为它实现了MakeCounter,因此有资格作为CounterMaker

    【讨论】:

    • 感谢您的回答。因此,如果我明白你在说什么,无论我采用哪种方法,我都将不得不为 17 个结构中的每一个重复枢轴逻辑。
    • 也许吧。这取决于你真正想要实现的目标,以及你真正拥有的东西。我使用的示例允许您将此逻辑放入每个实现(每种统计类型)中,但也许有一种完全不同的方法。如果没有,将逻辑放在定义 type 的代码中的好处是您不必有很大的类型切换,并且您可以添加更多类型,只要它们也实现界面。也就是说,接口操作为您提供类型安全。
    • 谢谢。感谢您的帮助,我不确定我是否正确地解释了自己。我正在尝试将Win32_PerfFormattedData_Tcpip_NetworkAdapterWin32_PerfFormattedData_Counters_ProcessorInformation(以及其他15 个结构)转换为一个看起来像我在Counter 结构定义中定义的结构。我发现我做了很多代码重复,尽管 Go 有“不要重复自己”的核心原则。所以我仍然认为我做错了。
    • 从根本上说,问题在于您已经拥有一些具有某种“形状”的实体类型(A、B、C、P 或 Q,或 1 至 17,或其他)。您正试图将每一个都转换为具有其他形状的其他东西(例如您的 E-A-V 模型)。这需要 17 个不同的代码块,因为您必须从类型 A(或 #1)中选择正确的东西,从类型 B(或 #2)中选择正确的东西等等。问题不在于需要多少代码,而在于,如何安排维修等。
    猜你喜欢
    • 1970-01-01
    • 2022-08-11
    • 1970-01-01
    • 2022-06-21
    • 2017-02-05
    • 1970-01-01
    • 2016-06-20
    • 1970-01-01
    • 2016-01-06
    相关资源
    最近更新 更多