在观看了一些关于 Go 的视频教程后,我想知道是否可以创建具有动态路由的管道。受 Go 中的 Builder 设计模式和 Optional Pattern 的启发,我想出了一个想法来实现它。
怎么做 ?
想象一下我们在一家汽车厂,一种车型有几种类型。在生产同一车型的汽车时,会有一些相同的制造步骤,例如底盘和车身的制造,然后每种类型都会添加一组不同的功能。我们将创建的管道有一个如下所示的流程图,在“车身”管道之后,可以直接构建汽车或根据其类型添加功能。
管道包
首先,我们将创建一个汽车结构,其中包含几个字段,例如基础、车身、特征 A、特征 B 和特征 C。之后,我们将创建几个方法来填充这些字段。
var Process = func(channel chan *CarBuilder) func(builder *CarBuilder) {
return func(builder *CarBuilder) {
channel <- builder
}
}
type NextProcess func(builder *CarBuilder)
type CarBuilder struct {
car
Next []NextProcess
}
func (c *CarBuilder) nextProcess() {
next := c.Next[0]
c.Next = c.Next[1:]
next(c)
}
func (c *CarBuilder) Build() car {
return c.car
}
之后,我们定义了一个受可选模式启发的名为“Process”的变量,一个结构“CarBuilder”成为汽车的构建器,其中包含汽车字段和我们要添加的功能列表。然后创建一个“nextProcess”方法来移动到下一个管道,并创建一个“Build”方法来返回我们建造的汽车。
var Process = func(channel chan *CarBuilder) func(builder *CarBuilder) {
return func(builder *CarBuilder) {
channel <- builder
}
}
type NextProcess func(builder *CarBuilder)
type CarBuilder struct {
car
Next []NextProcess
}
func (c *CarBuilder) nextProcess() {
next := c.Next[0]
c.Next = c.Next[1:]
next(c)
}
func (c *CarBuilder) Build() car {
return c.car
}
最后一个阶段是定义管道,我们将创建 5 个管道,分别是“BaseBuilder”、“BodyBuilder”、“FeatureABuilder”、“FeatureBBuilder”和“FeatureCBuilder”,用于创建汽车模型的基础或添加特征。
func BaseBuilder(channel <-chan *CarBuilder, nextChannel chan<- *CarBuilder) {
for {
carBuilder := <-channel
carBuilder.setBase(true)
nextChannel <- carBuilder
}
}
func BodyBuilder(channel <-chan *CarBuilder) {
for {
carBuilder := <-channel
carBuilder.setBody(true)
carBuilder.nextProcess()
}
}
func FeatureABuilder(channel <-chan *CarBuilder) {
for {
carBuilder := <-channel
carBuilder.setFeatureA(true)
carBuilder.nextProcess()
}
}
func FeatureBBuilder(channel <-chan *CarBuilder) {
for {
carBuilder := <-channel
carBuilder.setFeatureB(true)
carBuilder.nextProcess()
}
}
func FeatureCBuilder(channel <-chan *CarBuilder) {
for {
carBuilder := <-channel
carBuilder.setFeatureC(true)
carBuilder.nextProcess()
}
}
主功能
要在主函数(main.go)中使用它,首先我们定义一个“等待组”来等待所有进程完成,并为每个具有大缓冲区的管道定义一个通道以加快速度:D
var wg sync.WaitGroup
baseChan := make(chan *pipe.CarBuilder, 1000)
bodyChan := make(chan *pipe.CarBuilder, 1000)
featureAChan := make(chan *pipe.CarBuilder, 1000)
featureBChan := make(chan *pipe.CarBuilder, 1000)
featureCChan := make(chan *pipe.CarBuilder, 1000)
buildChan := make(chan *pipe.CarBuilder, 1000)
还要准备一份我们想要的汽车类型的清单以及这些类型的可用功能,以促进大规模制造汽车。“NextProcess” 切片包含表示我们创建类型所需的特征的通道。
typeStandard := []pipe.NextProcess{
pipe.Process(buildChan),
}
typeA := []pipe.NextProcess{
pipe.Process(featureAChan),
pipe.Process(featureCChan),
pipe.Process(buildChan),
}
typeB := []pipe.NextProcess{
pipe.Process(featureAChan),
pipe.Process(featureBChan),
pipe.Process(buildChan),
}
typeC := []pipe.NextProcess{
pipe.Process(featureBChan),
pipe.Process(featureCChan),
pipe.Process(buildChan),
}
typeFullFeature := []pipe.NextProcess{
pipe.Process(featureAChan),
pipe.Process(featureBChan),
pipe.Process(featureCChan),
pipe.Process(buildChan),
}
features := [][]pipe.NextProcess{
typeStandard, typeA, typeB, typeC, typeFullFeature,
}
之后,定义我们想要的工作池,我为每个管道使用 4 个,因为如果使用更多(在我的设备中),则不会显着增加。我们还可以定义一个汽车计数器来确定已经制造了多少辆汽车。
var carCount int64
for i := 1; i <= 4; i++ {
go pipe.BaseBuilder(baseChan, bodyChan)
go pipe.BodyBuilder(bodyChan)
go pipe.FeatureABuilder(featureAChan)
go pipe.FeatureBBuilder(featureBChan)
go pipe.FeatureCBuilder(featureCChan)
go func() {
for {
readyToBuild := <-buildChan
readyToBuild.Build()
wg.Done()
atomic.AddInt64(&carCount, 1)
}
}()
}
定义我们想要制造的汽车数量及其类型,在这种情况下,我们将制造 100000 辆汽车。
var testBuilds []pipe.CarBuilder
for i := 0; i < 1000000; i++ {
testBuilds = append(testBuilds, pipe.CarBuilder{
Next: features[i%5],
})
}
最后,将我们上面定义的汽车一个一个地放入第一个管道“BaseBuilder”中,让奇迹发生?
defer func() func() {
start := time.Now()
return func() { fmt.Println(time.Since(start), carCount) }
}()()
for _, testBuild := range testBuilds {
wg.Add(1)
t := testBuild
baseChan <- &t
}
wg.Wait(
在 go 中创建动态管道路由的关键是什么?
关键在于“Process”变量和“nextProcess”方法。在 process 变量中,我们可以输入要访问的管道的通道,而“nextProcess”方法会将 CarBuilder 指针传递给“Process”变量,这意味着它将 CarBuilder 指针发送到下一个管道。
var Process = func(channel chan *CarBuilder) func(builder *CarBuilder) {
return func(builder *CarBuilder) {
channel <- builder
}
}
type NextProcess func(builder *CarBuilder)
type CarBuilder struct {
car
Next []NextProcess
}
func (c *CarBuilder) nextProcess() {
next := c.Next[0]
c.Next = c.Next[1:]
next(c)
}
func (c *CarBuilder) Build() car {
return c.car
}
我希望你们中的一些人觉得这很有帮助。如果您有兴趣查看所有源代码,可以在 https://github.com/yudaph/dynamic-pipeline.