Die Nachteile der traditionellen prozeduralen Codierungsmethode liegen auf der Hand. Wir haben oft die Erfahrung gemacht, dass Codes, die über einen längeren Zeitraum nicht gepflegt wurden, plötzlich wiederkommen und die ursprüngliche Idee verstehen Diesmal kann es weniger Zeit in Anspruch nehmen, wenn ein gut geschriebenes Dokument oder eine gut geschriebene Anmerkung vorhanden ist. Trotzdem müssen viele Aufrufbeziehungen wiederholt bestätigt werden, bevor Änderungen vorgenommen werden können.
Das Folgende ist ein Pseudocode, der die prozedurale Codierungsmethode beschreibt:
func A(){ B() C() } func B(){ do something D() } func C(){ do something } func D(){ do something } func main(){ A() }
Vergleichen Sie die Schreibmethode des Streaming-Stils:
NewStream(). Next(A). Next(B). Next(D). Next(C). Go()
Wenn die Codeaufrufbeziehung im prozeduralen Stil komplex ist, müssen Programmierer vorsichtig und vorsichtig vorgehen Mit dem Streaming-Stil ist der Code relativ sauber und das Grundgerüst klar, was insbesondere beim Umgang mit geänderten Anforderungen offensichtliche Vorteile hat.
Java8 leiht sich Lambda-Ausdrücke aus, um einen relativ perfekten Streaming-Programmierstil zu implementieren. Als einfache Sprache verfügt Golang noch nicht über ein offizielles Streaming-Stil-Paket (es gibt es vielleicht schon lange, vielleicht bin ich unwissend). Mitleid. .
Ich habe auf den Code von gorequest verwiesen und ein relativ häufiges Streaming-Stilpaket implementiert. Das Implementierungsprinzip besteht darin, den ersten Knoten, den nächsten Knoten und den Rückruf zu speichern, den der Knoten ausführen soll Nachdem die Streaming-Aufgabe gestartet wurde, wird sie beginnend mit dem ersten Knoten einzeln ausgeführt. Wenn eine Ausnahme auftritt, wird die Streaming-Aufgabe beendet, bis die letzte ausgeführt wird, wodurch die Aufgabenkette beendet wird. Werfen wir zunächst einen Blick auf den Code:
package Stream import ( "errors" "fmt")/** 流式工作原理: 各个任务都过指针链表的方式组成一个任务链,这个任务链从第一个开始执行,直到最后一个 每一个任务节点执行完毕会将结果带入到下一级任务节点中。 每一个任务是一个Stream节点,每个任务节点都包含首节点和下一个任务节点的指针, 除了首节点,每个节都会设置一个回调函数的指针,用本节点的任务执行, 最后一个节点的nextStream为空,表示任务链结束。 **///定回调函数指针的类型type CB func(interface{}) (interface{}, error)//任务节点结构定义type Stream struct { //任务链表首节点,其他非首节点此指针永远指向首节点 firstStream *Stream //任务链表下一个节点,为空表示任务结束 nextStream *Stream //当前任务对应的执行处理函数,首节点没有可执行任务,处理函数指针为空 cb CB }/** 创建新的流 **/func NewStream() *Stream { //生成新的节点 stream := &Stream{} //设置第一个首节点,为自己 //其他节点会调用run方法将从firs指针开始执行,直到next为空 stream.firstStream = stream //fmt.Println("new first", stream) return stream }/** 流结束 arg为流初始参数,初始参数放在End方法中是考虑到初始参数不需在任务链中传递 **/func (this *Stream) Go(arg interface{}) (interface{}, error) { //设置为任务链结束 this.nextStream = nil //fmt.Println("first=", this.firstStream, "second=", this.firstStream.nextStream) //检查是否有任务节点存在,存在则调用run方法 //run方法是首先执行本任务回调函数指针,然后查找下一个任务节点,并调用run方法 if this.firstStream.nextStream != nil { return this.firstStream.nextStream.run(arg) } else { //流式任务终止 return nil, errors.New("Not found execute node.") } } func (this *Stream) run(arg interface{}) (interface{}, error) { //fmt.Println("run,args=", args) //执行本节点函数指针 result, err := this.cb(arg) //然后调用下一个节点的Run方法 if this.nextStream != nil && err == nil { return this.nextStream.run(result) } else { //任务链终端,流式任务执行完毕 return result, err } } func (this *Stream) Next(cb CB) *Stream { //创建新的Stream,将新的任务节点Stream连接在后面 this.nextStream = &Stream{} //设置流式任务链的首节点 this.nextStream.firstStream = this.firstStream //设置本任务的回调函数指针 this.nextStream.cb = cb //fmt.Println("next=", this.nextStream) return this.nextStream }
Das Folgende ist ein Streaming-Beispiel, das den Prozess vom Aufstehen am Morgen bis zur Arbeit als Beispiel nimmt:
//起床func GetUP(arg interface{}) (interface{}, error) { t, _ := arg.(string) fmt.Println("铃铃.......", t, "###到时间啦,再不起又要迟到了!") return "醒着的状态", nil }//蹲坑func GetPit(arg interface{}) (interface{}, error) { s, _ := arg.(string) fmt.Println(s, "###每早必做的功课,蹲坑!") return "舒服啦", nil }//洗脸func GetFace(arg interface{}) (interface{}, error) { s, _ := arg.(string) fmt.Println(s, "###洗脸很重要!") return "脸已经洗干净了,可以去见人了", nil }//刷牙func GetTooth(arg interface{}) (interface{}, error) { s, _ := arg.(string) fmt.Println(s, "###刷牙也很重要!") return "牙也刷干净了,可以放心的大笑", nil }//吃早饭func GetEat(arg interface{}) (interface{}, error) { s, _ := arg.(string) fmt.Println(s, "###吃饭是必须的(需求变更了,原来的流程里没有,这次加上)") return "吃饱饱了", nil }//换衣服func GetCloth(arg interface{}) (interface{}, error) { s, _ := arg.(string) fmt.Println(s, "###还要增加一个换衣服的流程!") return "找到心仪的衣服了", nil }//出门func GetOut(arg interface{}) (interface{}, error) { s, _ := arg.(string) fmt.Println(s, "###一切就绪,可以出门啦!") return "", nil } func main() { NewStream(). Next(GetUP). Next(GetPit). Next(GetTooth). Next(GetFace). Next(GetEat).//需求变更了后加上的 Next(GetCloth). Next(GetOut). Go("2018年1月28日8点10分") }
Aus dem obigen Code ist der Streaming-Codierungsstil Nach der Bildung mehrerer kleiner Aufgaben müssen Sie sich nicht mehr darum kümmern, welche Aufgaben aufgerufen werden. Im obigen Beispiel ist es einfacher, sie zu ändern Das Frühstück war in der ersten Version nicht implementiert. Der Kunde sagte, er müsse morgens essen, sonst würde er leicht Gallensteine entwickeln. Die zweite Version muss hinzugefügt werden und dann zur Antwortposition hinzugefügt werden . Im Vergleich zur prozeduralen Codierung ist es viel einfacher.
Weitere technische Artikel zum Thema Golang finden Sie in der GolangTutorial-Kolumne!
Das obige ist der detaillierte Inhalt vonMinimalistische Flow-Programmierung in Golang. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!