[译]Go开发中一些有用的模式
原文: Some useful patterns by Bob.
从 VB.net、Java、C# 和 Python 开始转到 Go开发的时候,我对Go语言层级的模式的缺乏有点懊恼,这促使我花了一点时间找出容易表达的那些模式。
这里是一些通用的模式的集合,以及我发现的最容易表示它们的方式。
装饰器(Decorator)
这个特性在大部分的编程语言中都有广泛的应用, 使用某种效果或者属性来加强一个函数或者方法的功能。
如果你熟悉python, 你可能属性下面的代码:
|
|
或者c#中类似的代码:
|
|
这段代码是说当你访问/private
路径的数据时,你需要身份验证。
上面的这种编程风格有几个危险而令人讨厌的问题:
很容易忘记注解(annotation),或者放错地方
为了理解代码的正确性,你需要额外地了解这种编程语言的特性(比如注解顺序是否有相关性)
不容易发现注解定义的地方
控制逻辑隐藏在注释中
Go使用另外一种方式:
|
|
一个简单的Authenticate
实现如下:
|
|
此外,这种模式允许Go开发者装饰整个类型,而不仅仅是函数。你可以为(http.ServeMux).ServeHTTP
增加点东西,例如增加一些缺省的Header:
|
|
这个例子在初始阶段发现缺少的身份认证调用是很容易的:所有的handler都在一个函数中隐式地调用,而不是handler定义的地方所有的路由都在一个地方进行处理,这样你就不容易遗漏。
单例(Singleton)
单例是一个通用的表达方式,来表示只存在程序中某个地方的某个东西。它可以延迟初始化,也可以启动时就初始化,依赖这个值何时初始化。非延迟初始化的单例一般实现为全局变量,它们可以在 init
函数中初始化,或者在声明的时候初始化:
|
|
如果没有初始化的前置条件,用下面的方式更好:
|
|
这很平常, 我以前使用的大部分语言都这样做。 唯一的一个大的不同点是 Java/C#中这个变量需要是一个类的静态变量(static)。
Go保证 init 函数会在 main 函数之前被执行,所以可以保证这些值可以在使用之前已经被初始化了。
我们来谈谈延迟初始化(lazy),Java中的方式如下:
|
|
我看到这样的写法有几百次了,但是这里有个问题: 这里有个并发问题。如果多个并发访问getSingle, 这个值可能被初始化多次,也就不再是单例了。为了解决这个问题,需要加上synchronized
关键字,但是这又影响性能。
译者按: 这个Java中的一个经典问题, Java实现正确的单例模式至少有5中写法: 静态变量初始化、synchronized、双重检查、静态内部类、枚举类型
这种模式在Go中可以使用sync.Once
实现:
|
|
这有三个好处:
保证有且只有一次调用初始化
并发访问会被阻塞,知道初始化完成
初始化完成后调用很快
如果你对性能比较关心,可能注意到两个月前的一个更新可以将方法调用减少到 0.5纳秒。
使用装饰模式和单例模式,你可以巧妙地将一个不线程安全的API转换成安全的API。
例如, exec.Cmd
不允许调用Wait
方法多次,并发访问的时候怎么办呢?你可以像这样进行包装:
|
|
静态成员(static member)
这个模式只用在很稀少的场景,你需要某个struct类型的所有的实例需要共享同一个值。 我最近偶然发现的一个例子是出于调试目的:一个接口I
实现了Kind() string
函数,用来在日志系统中打印类型。
这太容易实现了,而且也不会弄乱包命名空间:
|
|
注意Kind()
需要一个指针类型的receiver,但是它并没有为这个receiver命名,所以很清晰的表明我们并不使用类型实例。
信号量(Semaphore)
因为我一直对并发模式感兴趣,我很惊讶Go居然没有信号量类型。
当然使用channel来模拟信号量是很容易的:
|
|
注意信号量实现了sync.Locker
接口。这段代码工作很正常, 往一个 unbuffered channel中发送一个值会被阻塞,知道其它的goroutine调用了Unlock方法,从channel中读取了这个值。如果channel的buffer设置为1, 我们就取得了类似Mutex的效果。
一个更有效带权重的实现可以在Go的实验包找到。
错误组(Errgroup)
有时候你想创建多个goroutine,让它们并行地工作,当遇到某种粗五或者你不像再输出了,你可能想取消整个goroutine。
为了取得这个效果你可以使用 sync.Waitgroup 和 context.Context 是可行的,但是这得需要很多冗余代码,我建议使用errgroup.Errgroup。
例子:
|
|
建议继续学习:
- Go Reflect 性能 (阅读:9720)
- 从Go看,语言设计(一) (阅读:4720)
- Go 语言初步 (阅读:3431)
- go-kit 入门(一) (阅读:3472)
- 为什么我们要使用Go语言以及如何使用它的 (阅读:3056)
- 软件架构模式的种类 (阅读:2841)
- 从Go看,语言设计(二) (阅读:2860)
- 让生活变简单的简单网站 (阅读:2237)
- Go 语言初学实践(1) (阅读:2183)
- lua cothread (阅读:2025)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:smallnest 来源: 鸟窝
- 标签: go 模式
- 发布时间:2019-04-08 00:59:05
- [67] Go Reflect 性能
- [67] Oracle MTS模式下 进程地址与会话信
- [67] 如何拿下简短的域名
- [61] IOS安全–浅谈关于IOS加固的几种方法
- [60] 图书馆的世界纪录
- [59] 【社会化设计】自我(self)部分――欢迎区
- [58] android 开发入门
- [56] 视觉调整-设计师 vs. 逻辑
- [49] 给自己的字体课(一)——英文字体基础
- [47] 界面设计速成