百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

设计模式中学习golang高级特性(一)

toyiye 2024-05-19 19:35 19 浏览 0 评论

序言

每种编程语言都有其独特的语法,而特定的语法也反映了该编程语言被创建之初的意图,即为了解决某种旧语言的一些痛点。比如说Golang的发明,Google公司最开始还是使用C++来做工程开发,但C++有一些明显的痛点,比如说编译速度很慢,大型项目的一次构建长达2小时以上,同时还存在内存泄漏的风险,其次对并发的支持也不是很好,那么Google的几个大佬在C++语言的基础上,进行了一些修正,具体可见于Rob Pike关于go设计思路的那封邮件。

本文不想直接的罗列出Golang的一堆高级特性,然后依次讲解,而想换一种思路,结合大牛们基于Go语言实现的设计模式的代码,让大家直观的感受Go语言高级特性实战的用法和Golang的设计哲学,less is more!

简单工厂模式

语法关键词:接收者函数,接口类型interface,空结构体struct{}

那么开始,我们实现一个简单工厂模式,它的格式一般是通过入参枚举的不同,返回特定的类,然后这些类都是有一个同名函数的。比如说苹果,橘子,桃子三个产品,然后要有一个show函数告诉我一斤多少钱啊,如果要是C++写的话,那肯定要定义一个基类和一个纯虚函数,子类重写该函数即可。

但是Go是没有类的概念,当然也就没有类构造函数和类方法,对于Go来说只有普通的函数,这省去了复杂的继承和派生的逻辑,Go设计认为用正交组合的方式去组织代码要更加简单解耦。Go为了实现类似类方法和多态的感觉,引入了接收者函数和接口类型interface,接收者函数比C++那种普通函数声明多了一处不同的就是函数名前面多了一个叫接收器的东西,它定义这些函数将在其上运行的对象的一种约定格式,即类似Java接口的感觉:

那么,我们看Go语言具体如何实现简单工厂模式:

package simplefactory

import (
	"fmt"
)

// interface代表接口类型,Fruit都有一个展示价格的函数
type Fruit interface {
	HowMuch()
}

func NewFruit(t int) Fruit {
	if t == 1 {
		return &Apple{}
	} else if t == 2 {
		return &Banana{}
	}
	return nil
}

type Apple struct{}

func (*Apple) HowMuch() {
	fmt.Printf("Hi, Apple 一块钱 一斤\n")
}

type Banana struct{}

func (*Banana) HowMuch() {
	fmt.Printf("Hi, Banana 九毛钱 一斤\n")
}

单例模式

语法关键词:包内函数和变量的私有化,sync.Once,协程,chan,等待组

单例模式的实现老生常谈啦,一面的时候总考,我一波构造函数私有化+互斥锁+double check全给他防出去了,那么Go语言实现这个模式会是怎样的呢?首先函数私有的问题,Go语言不像C++有 public、protected、private 等访问控制修饰符,其次Go舍弃了C++中include头文件的习惯,而是引入类似于python的import包,因此,Go是通过字母大小写以及下划线开头来控制可见性的,大写字母开头表示能被其它包访问或调用(相当于 public),非大写开头表示只能在包内使用(相当于 private)

package test_private_and_public

const (
	PubicVar    = `大写变量开头,这是一个公开的常量`
	privateVar  = `小写变量开头,这是一个私有的常量`
	_privateVar = `下划线开头,  这是一个私有的常量`
)

func PubicFuc() {
	println(`这是一个公开的函数`)
}

func privateFuc() {
	println(`这是一个私有的函数`)
}

func _privateFuc() {
	println(`这也是一个私有的函数`)
}

这样我们就可以把单例的结构体搞成小写的,这样外面引用的人只能使用我们的公开的构造函数去创建对象,而不会直接自己就可以new啦。

其次,只执行一次是怎么做到呢?答案是用到了sync.Once这个特性。sync.Once 是 Go 标准库提供的使函数只执行一次的实现,可以在代码的任意位置初始化和调用,因此可以延迟到使用时再执行,并发场景下是线程安全的。

到了这步,单例模式的Go实现呼之欲出了,但是我们如何测试并发的场景呢,即同时要有多个线程让他都调用构造函数得到一个金斧子,然后等他们都得到一个金斧子单例的时候(这要考虑同步过程),再验证这些金斧子都是同一把呢?

并发的解决方法便是利用go协程,它是在应用层模拟的线程,他避免了上下文切换的额外耗费,兼顾了多线程的优点,这也是应了Golang的设计之初的目的就是为了解决C++并发的复杂性。go协程开启通过关键字 go 启用多个协程,然后在不同的协程中完成不同的子任务,这些用户在代码中创建和维护的协程本质上是用户级线程,用户代码的执行最后还是要落到系统级的线程中的,其内部是维护了一组数据结构, n 个线程和一个待执行队列。协程的切换是golang利用系统级异步 io函数的封装,这些封装的函数提供给应用程序使用,当这些异步函数返回 busy 或 bloking 时,golang 利用这个时机将现有的执行序列压栈,让线程去拉另外一个协程的代码来执行,这达到的协程切换的目的。

由于golang是从编译器和语言基础库多个层面对协程做了实现,所以,golang的协程是目前各类有协程概念的语言中实现的最完整和成熟的。十万个协程同时运行也毫无压力。

好啦,现在并发的条件有了,那么我们需要有协程同步的机制,大家得凝成一个势力才可以发挥更大的能量,这里利用的是特性是chan等待组,等待组和C++中的信号量很像,通过PV操作达到同步的机制。chan是Go中专属的叫通道,它是一个先进先出的队列,入队和出队会阻塞的作用,他的声明分为以下四种:

// 不带缓冲的通道,它里面可以放字符串类型,进和出都会阻塞。
ch1 := make(chan string)

// 队列长度为5个的通道,它里面可以放字符串类型,如果通道内元素达到队列长度时,再进就会阻塞。
ch2 := make(chan string, 5)

// 只读通道
ch3 := make(<-chan string)

// 只写通道
ch4 := make(chan<- string)

一个chan的小李子:

package main

import (
    "fmt"
)

func main() {
    // 构建一个通道
    ch := make(chan int)
    // 开启一个并发匿名函数
    go func() {
        fmt.Println("start goroutine")
        // 通过通道通知main的goroutine,接受之前我阻塞在这里
        ch <- 0
        fmt.Println("exit goroutine")

    }()
    fmt.Println("wait goroutine")
    // 读取通道的值,如果没有就阻塞
    <-ch
    fmt.Println("all done")
}
//运行结果
PS D:\code\golang-design-pattern\03_singleton> go run .\2n.go
wait goroutine
start goroutine
exit goroutine
all done

准备工作都做好了,那么让我们开始写一个单例模式吧。

//实现部分
package singleton

import (
	"fmt"
	"sync"
)

// 包私有,该结构体不能直接使用
type goldaxe struct{}

func (axe goldaxe) TellTruth() {
	fmt.Printf("小伙汁你太诚实啦,金斧子是你的了\n")
}

// 全局变量
var (
	axe  *goldaxe
	once sync.Once
)

// 由于单例类型不能在包外直接使用,用一个接口类型带出去
type GoldAxe interface {
	TellTruth()
}

// 用于获取单例模式对象,大家都是一样的斧子
func GetGoldAxe() GoldAxe {
	once.Do(func() {
		axe = &goldaxe{}
	})

	return axe
}

//测试部分
package singleton

import (
	"sync"
	"testing"
)

const axeCounts = 100

func Test1(t *testing.T) {
	ins1 := GetGoldAxe()
	ins1.TellTruth()
	ins2 := GetGoldAxe()
	if ins1 != ins2 {
		t.Fatal("instance is not equal")
	}
}

func Test2(t *testing.T) {
	start := make(chan struct{})
	//信号量初始化
	wg := sync.WaitGroup{}
	//信号量搞个100个
	wg.Add(axeCounts)
	//金斧子数组,并进行了列表初始化
        //这么写你紫定get了:var float_array = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
	instances := [axeCounts]GoldAxe{}
	for i := 0; i < axeCounts; i++ {
		//开启100个协程
		go func(index int) {
			//获取channel的值,由于没有协程只能阻塞
			<-start
			instances[index] = GetGoldAxe()
			wg.Done()
		}(i)
	}
	//关闭channel,所有协程同时GetGoldAxe,达到了并发创建实例的情况
	close(start)
	//等待大家都获得自己金斧子后,才到下一步
	wg.Wait()
	for i := 1; i < axeCounts; i++ {
		if instances[i] != instances[i-1] {
			t.Fatal("instance is not equal")
		}
	}
}

Reference

mohuishou/go-design-pattern: golang design pattern go 设计模式实现,包含 23 种常见的设计模式实现,同时这也是极客时间-设计模式之美 的笔记 (github.com)

Go sync.Once | Go 语言高性能编程 | 极客兔兔 (geektutu.com)

Go 语言数组 | 菜鸟教程 (runoob.com)

[系列] Go - chan 通道 - 新亮笔记 - 博客园 (cnblogs.com)

Go语言等待组(sync.WaitGroup) (biancheng.net)

Golang 之协程详解 - 星火燎原智勇 - 博客园 (cnblogs.com)

Go 语言并发编程系列(二)—— Go 协程实现原理和使用示例 - 腾讯云开发者社区-腾讯云 (tencent.com)

Go语言通道(chan)——goroutine之间通信的管道 (biancheng.net)

Go 接口类型 - 云崖先生 - 博客园 (cnblogs.com)

Go语言:公开和私有化的属性和函数 - 简书 (jianshu.com)

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码