-1

您好,我有 2 个看起来相似的函数,我想创建一个通用函数。我的问题是:我不确定如何传入另一个函数:

func (b *Business) StreamHandler1(sm streams.Stream, p []*types.People) {
    guard := make(chan struct{}, b.maxManifestGoRoutines)
    for _, person := range p {
        guard <- struct{}{} // would block if guard channel is already filled
        go func(n *types.People) {
            b.PeopleHandler(sm, n)
            <-guard
        }(person)
    }
}

func (b *Business) StreamHandler2(sm streams.Stream, pi []*types.PeopleInfo) {
    guard := make(chan struct{}, b.maxManifestGoRoutines)
    for _, personInfo := range pi {
        guard <- struct{}{} // would block if guard channel is already filled
        go func(n *types.PeopleInfo) {
            b.PeopleInfoHandler(sm, n)
            <-guard
        }(personInfo)
    }
}

你可以看到它们看起来非常非常相似,所以我想做一个可以传入的通用函数PeopleInfoHandler PeopleHandler 。知道如何正确地做到这一点吗?看起来像 Go 的语法我应该能够做这样的事情:

func (b *Business) StreamHandler1(f func(streams.Stream, interface{}), sm streams.Stream, p []*interface{}) {

但这似乎不起作用。关于如何使这个通用的任何想法?

4

2 回答 2

1

您可以使用定义的特定接口类型为您传递的类型创建抽象。

我使用Peopler接口获取 People 或 PeopleInfo,基于我定义并传递给新 StreamHandler 的处理程序。*Business如果您需要它的任何字段/方法,您也可以在处理程序中传递。

但正如 Sergio 所说,如果该方法只有 5 行长,即使大体相同,也可能不值得。

对于带有保护结构的模式,您可以使用sync.WaitGroup更适合的模式。

package main

import (
    "fmt"
    "time"
)


func (b *Business) StreamHandler(sm streamsStream, p []Peopler, handler func(streamsStream, Peopler)) {
    guard := make(chan struct{}, b.maxManifestGoRoutines)
    for _, person := range p {
        guard <- struct{}{} // would block if guard channel is already filled
        go func(p Peopler) {
            handler(sm, p)
            <-guard
        }(person)
    }
}

func peopleInfoHandler(s streamsStream, p Peopler) {
    fmt.Println("info:", p.PeopleInfo())
}

func peopleHandler(s streamsStream, p Peopler) {
    fmt.Println("people:", p.People())
}

func main() {
    b := &Business{maxManifestGoRoutines: 2}
    s := streamsStream{}
    p := []Peopler{
        &People{
            Info: PeopleInfo{Name: "you"},
        },
    }
    b.StreamHandler(s, p, peopleInfoHandler)
    b.StreamHandler(s, p, peopleHandler)

    time.Sleep(time.Second)
}

type streamsStream struct {
}

type People struct {
    Info PeopleInfo
}

func (tp *People) People() People {
    return *tp
}

type PeopleInfo struct {
    Name string
}

func (tp *People) PeopleInfo() PeopleInfo {
    return tp.Info
}

type Peopler interface {
    People() People
    PeopleInfo() PeopleInfo
}

type Business struct {
    maxManifestGoRoutines int
}

播放链接

于 2021-02-03T00:50:44.710 回答
0

如果可能,您可以使用依赖倒置来使用接口。类型和函数之间的关联几乎就是方法的定义。所以使用一个接口来指定方法定义,并将 Business 传递给该方法。

该接口对于防止导入周期以及使代码更宽松是必要的。

通常使用依赖反转,处理程序将在与 Business 相同的包中显式实现接口,但这在 Go 中都是隐含的。

例如:

type Handler interface {
    Handle(*Business, streams.Stream)
}

func (b *Business) StreamHandler1(sm streams.Stream, hs []Handler) {
    guard := make(chan struct{}, b.maxManifestGoRoutines)
    for _, h := range hs {
        guard <- struct{}{} // would block if guard channel is already filled
        go func(n Handler) {
            n.Handle(b, sm)
            <-guard
        }(h)
    }
}

或者,如果您需要该类型的特定功能,您可以让 Business 方法接受该行为的接口。这会更优雅一些,但需要提前跨多种类型或大型重构进行更多计划。例如:

type TypeDoThinger interface {
    Type() Schema
    DoThing() ImportantValue
}

func (b *Business) HandleTypeDoThinger(sm streams.Stream, t TypeDoThinger) {
    sch := t.Type()
    // use schema for something
    v := t.DoThing()
    // save the important data
}

func (b *Business) StreamHandler(sm streams.Stream, ts []TypeDoThinger) {
    guard := make(chan struct{}, b.maxManifestGoRoutines)
    for _, t := range ts {
        guard <- struct{}{} // would block if guard channel is already filled
        go func(t TypeDoThinger) {
            b.HandleTypeDoThinger(sm, t)
            <-guard
        }(t)
    }
}
于 2021-02-03T00:51:27.297 回答