反模式(anti-pattern或antipattern)又叫做反面模式,指的是在实践中经常出现但又低效或是有待优化的设计模式,是用来解决问题的带有共同性的不良方法。Andrew Koenig在1995年造了anti-pattern这个词,灵感来自于GoF的《设计模式》一书。
按《AntiPatterns》作者的说法,可以用至少两个关键因素来把反面模式和不良习惯、错误的实践或糟糕的想法区分开来:
- 行动、过程和结构中的一些重复出现的乍一看是有益的,但最终得不偿失的模式
- 在实践中证明且可重复的清晰记录的重构方案
维基百科上列出了一些反模式列表: 反面模式, 我开了☝系列,用来记录Go语言开发中的一些反模式。
这是第一篇,介绍 越俎代庖
反模式,或者叫做画蛇添足
反模式,或者叫做镀金
反模式(Gold plating
)。 意思是指项目已经达到了设计的最高价值,结果还添加额外的功能,反而使项目变得很差。
当然,本文以及后续文章中的实例可能会引起争议,欢迎在评论中讨论。你如果也发现了一些Go的反模式,也欢迎留言。
glog例子
golang/glog,这是Google开源的一个log库,可以实现多级的log日志输出。它实现了google/glog相同行为的日志输出。
项目介绍说这个项目的源代码master在Google内部。github上的目前处于不维护的状态,最新同步都是四年前了。
首先,我们说一下这个库的好处,简单好用,可以根据日志级别进行设置,而且带文件输出功能。
你可以写一个简单的程序测试一下:
|
|
然后运行go run main.go
看看效果。
什么?没有任何日志输出,再尝试go run main.go -stderrthreshold INFO
试试:
|
|
这次终于看到日志了。
如果你运行go run main.go
,你会看到你的程序莫名其妙的加了几个参数:
|
|
这就是我们介绍的反模式。本来glog作为一个库提供给其它人使用,但是却额外偷偷的在命令行参数中注入了几个参数,这种强迫并且非显式的方式并不是作为库的好的行为。
这种方式并没有在库的使用者允许的情况下就注入额参数,一是污染了使用者的命令行解析方式,二是给使用者一个风险提示,这个库是否是安全的,会不会偷偷注入恶意代码?
更大的问题是命令行参数冲突。 假设你要为你的程序提供一个查看版本的功能,使用者可以使用main -v
显示版本号:
|
|
如果你运行上面的程序,会panic失败:
|
|
原因在于glog定义了一个v
参数,而你也定义了一个v
参数,导致冲突。可是v
是很通用的一个查看版本的参数,这也意味着你不得不改个参数名称。
很显然,glog
库把一些本来使用者需要决定的事情给实现了,本来移除掉这些代码,或者单独抽取出独立的函数,或者使用者可以定制参数,都是比这种私自决定的方式好很多。
同样的,vitess也有同样的问题。
当然,vitess作为一个独立的工具,而不是库来说,问题不大,因为代码不会作为库使用。但是实际上很多项目也使用vitness的代码,这也会导致问题。
pprof
另一个非常常见的例子是对runtime/pprof
的误用。
你已知道,go本身自带的pprof
库非常的有用,如果你通过_ "net/http/pprof"
引入了pporf,可以方便的跟踪Go程序性能和资源占用的问题。甚至你还可以自己定义开发自有的pprof
,比如下面的文章中介绍的:
- Performance and memory analysis of Golang programs
- book: Concurrency in go
- Creating custom Go profiles with pprof
基本上,你需要实现类似的代码(摘自上面最后一个链接的例子):
|
|
但是不幸的是,很多文章中并没有提及使用这个方式带来的副作用。
本来,如果你在你的程序中使用类似的代码并没有问题,但是如果你正在开发一个库,使用这段代码的话,相当于往profiles🀄️强制注册了一个Profile,不管别人用或不用。当别人使用_ "net/http/pprof"
的时候,就会展示你的Profile。