golang的极简流式编程
传统的过程编码方式带来的弊端是显而易见,我们经常有这样的经验,一段时间不维护的代码或者别人的代码,突然拉回来看需要花费较长的时间,理解原来的思路,如果此时有个文档或者注释写的很好的话,可能花的时间会短一点,但是即便如此,很多调用关系也要反复确认才敢动手改动。
下面是一断伪代码,描述过程编码方式:
func A(){ B() C() } func B(){ do something D() } func C(){ do something } func D(){ do something } func main(){ A() }
对照流式风格的写法:
NewStream(). Next(A). Next(B). Next(D). Next(C). Go()
当过程风格的代码调用关系复杂时,程序员需要谨慎仔细行事,相比较流式风格的代码比较清爽,主干清晰,尤其是应对需求变更的时候优势明显。
java8里借用lamda表达式实现了一套比较完美的流式编程风格,golang作为一个简洁的语言还没有官方的流式风格的包(可能早就有了,可能是我孤陋寡闻了)有点可惜了。
我参考了gorequest的代码,实现了一套相对比较通用的流式风格的包,实现原理是组成一个任务链表,每一个节点都保存了首节点和下一个节点以及该节点应该执行的回调函数指针,流式任务启动后从第一个节点开始,逐个执行,遇到异常则终止流式任务,直到执行到最后一个,结束任务链。先来看看代码吧:
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 }
下面是一个流式的例子,这里以早上起床到出门上班的流程为例:
//起床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分") }
从上面的代码看,流式编码风格对于一个大的任务被分解成多个小任务后,在代码层面是非常直观的,不在费劲心思去查找到底那个调用了那个,另外对于需求的变更更容易了,上例中的吃早饭是第一个版本没有实现的,客户说了早上要吃饭,不然容易的胆结石,第二版要加上,我们需要完成吃饭的函数,然后加到响应的位置。相对过程编码简单了不少。
更多golang相关技术文章,请访问golang教程栏目!
Atas ialah kandungan terperinci golang的极简流式编程. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



Membaca dan menulis fail dengan selamat dalam Go adalah penting. Garis panduan termasuk: Menyemak kebenaran fail Menutup fail menggunakan tangguh Mengesahkan laluan fail Menggunakan tamat masa konteks Mengikuti garis panduan ini memastikan keselamatan data anda dan keteguhan aplikasi anda.

Bagaimana untuk mengkonfigurasi pengumpulan sambungan untuk sambungan pangkalan data Go? Gunakan jenis DB dalam pakej pangkalan data/sql untuk membuat sambungan pangkalan data untuk mengawal bilangan maksimum sambungan serentak;

Data JSON boleh disimpan ke dalam pangkalan data MySQL dengan menggunakan perpustakaan gjson atau fungsi json.Unmarshal. Pustaka gjson menyediakan kaedah kemudahan untuk menghuraikan medan JSON dan fungsi json.Unmarshal memerlukan penuding jenis sasaran kepada data JSON unmarshal. Kedua-dua kaedah memerlukan penyediaan pernyataan SQL dan melaksanakan operasi sisipan untuk mengekalkan data ke dalam pangkalan data.

Perbezaan antara rangka kerja GoLang dan rangka kerja Go ditunjukkan dalam seni bina dalaman dan ciri luaran. Rangka kerja GoLang adalah berdasarkan perpustakaan standard Go dan meluaskan fungsinya, manakala rangka kerja Go terdiri daripada perpustakaan bebas untuk mencapai tujuan tertentu. Rangka kerja GoLang lebih fleksibel dan rangka kerja Go lebih mudah digunakan. Rangka kerja GoLang mempunyai sedikit kelebihan dalam prestasi dan rangka kerja Go lebih berskala. Kes: gin-gonic (rangka Go) digunakan untuk membina REST API, manakala Echo (rangka kerja GoLang) digunakan untuk membina aplikasi web.

Fungsi FindStringSubmatch mencari subrentetan pertama dipadankan dengan ungkapan biasa: fungsi mengembalikan hirisan yang mengandungi subrentetan yang sepadan, dengan elemen pertama ialah keseluruhan rentetan dipadankan dan elemen berikutnya ialah subrentetan individu. Contoh kod: regexp.FindStringSubmatch(teks,corak) mengembalikan sekeping subrentetan yang sepadan. Kes praktikal: Ia boleh digunakan untuk memadankan nama domain dalam alamat e-mel, contohnya: e-mel:="user@example.com", pattern:=@([^\s]+)$ untuk mendapatkan padanan nama domain [1].

Laluan Pembelajaran Backend: Perjalanan Eksplorasi dari Front-End ke Back-End sebagai pemula back-end yang berubah dari pembangunan front-end, anda sudah mempunyai asas Nodejs, ...

Menggunakan zon waktu yang dipratentukan dalam Go termasuk langkah berikut: Import pakej "masa". Muatkan zon waktu tertentu melalui fungsi LoadLocation. Gunakan zon waktu yang dimuatkan dalam operasi seperti mencipta objek Masa, menghuraikan rentetan masa dan melaksanakan penukaran tarikh dan masa. Bandingkan tarikh menggunakan zon waktu yang berbeza untuk menggambarkan aplikasi ciri zon waktu yang telah ditetapkan.

Soalan Lazim pembangunan rangka kerja Go: Pemilihan rangka kerja: Bergantung pada keperluan aplikasi dan pilihan pembangun, seperti Gin (API), Echo (boleh berskala), Beego (ORM), Iris (prestasi). Pemasangan dan penggunaan: Gunakan arahan gomod untuk memasang, mengimport rangka kerja dan menggunakannya. Interaksi pangkalan data: Gunakan perpustakaan ORM, seperti gorm, untuk mewujudkan sambungan dan operasi pangkalan data. Pengesahan dan kebenaran: Gunakan pengurusan sesi dan perisian tengah pengesahan seperti gin-contrib/sesi. Kes praktikal: Gunakan rangka kerja Gin untuk membina API blog ringkas yang menyediakan POST, GET dan fungsi lain.
