经常能看到类似 newRouter
这样的代码
type router struct {
roots map[string]*node
handlers map[string]HandleFunc
}
func newRouter() *router {
return &router{
roots: make(map[string]*node),
handlers: make(map[string]HandlerFunc),
}
}
func (r *router) AddRoute(method string, pattern string, handler HandleFunc)
func (r *router) getRoute(method string, path string) (*node, map[string]string)
然后在其它地方这样调用
r := newRouter()
r.AddRoute()
为啥不直接这样呢?
var r router
代码都能看懂,但是不知道为啥喜欢这样用,或者这样用的好处是啥?
我能想到的是:
还有其它的好处吗?
在其它项目中还会看到这样的代码
package gulu
// ..........
// Result represents a common-used result struct.
type Result struct {
Code int `json:"code"` // return code
Msg string `json:"msg"` // message
Data interface{} `json:"data"` // data object
}
// NewResult creates a result with Code=0, Msg="", Data=nil.
func (*GuluRet) NewResult() *Result {
return &Result{
Code: 0,
Msg: "",
Data: nil,
}
}
package controller
// ....
func addSymArticleAction(c *gin.Context) {
result := gulu.Ret.NewResult()
defer c.JSON( http.StatusOK, result)
arg := map[string]interface{}{}
if err := c.BindJSON(&arg); nil != err {
result.Code = util.CodeErr
result.Msg = "parses add article request failed"
return
}
// ....
}
1
Trim21 2020-08-03 11:54:13 +08:00 via iPhone
用来给属性赋默认值…
|
2
ahmcsxcc 2020-08-03 11:56:07 +08:00
map 不赋值会 painc 啊
|
3
coderxy 2020-08-03 11:58:49 +08:00
map 要初始化的,不然会报错
|
4
janxin 2020-08-03 12:05:14 +08:00 1
你举的例子应该没有这样实际运行过,上面说的 panic 问题主要是针对 map 之类的特殊类型
另外一个一些必须进行初始化操作因为没有初始化方法的原因需要通过这种方式实现,比如默认值为非空。 当然并不是所有的情况都是需要这样进行的,比如官方库提供的 sync.Mutex 也是直接使用的 |
5
Jirajine 2020-08-03 12:05:57 +08:00 via Android 10
这属于 go 的糟粕之一。
go 号称所有类型创建时就会默认初始化,但类型只要包含引用这个默认的初始化值基本上就没法用(像这里 map 被初始化成 nil 而不是一个空的 map )。 于是 go 在编译时不进行静态检查而是给你一个“脏的初始值”,你那样用编译时没问题但跑着跑着就炸了。 |
6
BingoXuan 2020-08-03 12:08:41 +08:00
类似于构造函数,直接声明创建变量没问题,但是变量初始值怎么确定是问题。
|
7
ysc3839 2020-08-03 12:21:55 +08:00 via Android
个人理解:这里的 map 类似于 C++ 中的 unique_ptr,它可以自动 delete,但不能自动 new 。
感觉根本问题是没有构造函数,只能用一个普通函数模拟构造函数。 |
8
jybox 2020-08-03 12:32:47 +08:00 1
简单来说就是代替构造函数
|
9
huweic 2020-08-03 13:07:03 +08:00
过度解读一下,除了 map 初始化的问题,同时 New 的好处是为了方便以后扩展维护,指定默认值,执行其他初始化操作等,先把口子留着
|
10
zjsxwc 2020-08-03 13:14:02 +08:00
指针空值问题,
总要有个初始化的构造函数吧 |
11
wangritian 2020-08-03 13:17:24 +08:00
这是我不喜欢 go 的一个地方,没有构造函数,方法没有默认参数
|
12
ss098 2020-08-03 13:51:49 +08:00
Go 语言没有类,同样也没有构造器语言特性。
NewType 是 Go 语言的构造器命名惯例,等同于 Python __init__,PHP __construct 。 |
13
araraloren 2020-08-03 14:02:28 +08:00
所以为啥不弄一个 new 之类的构造函数。。
go 开发团队:为了保持解析器简单 :D |
15
virusdefender 2020-08-03 14:15:18 +08:00
没法强制指定构造函数确实是我不太喜欢的一点
|
16
hst001 2020-08-03 14:34:40 +08:00 via Android
这是一种类似工程规范的东西,普通的 NewXxx,有些会有多个初始变体会写成类似 NewXxxWithAaa 或 NewXxxFromAaa 之类的可读形式,隐藏掉没必要暴露的细节,无论对于调用者还是开发者,都可以提高可维护性。
|
17
zivn 2020-08-03 14:35:18 +08:00 1
1. 对结构体所有的属性进行必要的初始化,避免 panic ;
2. 方便做一些预处理,初始化函数可以传入参数,也可以返回错误,和 __init__ 、__construct 还不太一样; 另外 map 和 slice 底层定义都是结构体,一堆属性和指针的结合,区别在于 slice 的 append 函数可以自动初始化和扩容底层的数组结构,而 map 是直接 key-value 赋值的,不初始化底层结构,所以用 var 创建的 slice 一般可以正常使用,而 map 不行,会 panic,这个不算脏的初始值,也算不上是糟粕; |
18
hakono 2020-08-03 14:37:40 +08:00 3
上面虽然基本都提到了,但是用更简单易懂的方法来说明的话,那就是因为 Go 语言的类( struct )不存在构造函数这种东西,导致创建类的时候做不到普通语言那样 MyClass(para1, para2, para3,...) 。
所以如果你想在创建类的时候初始化一些什么东西的时候,就只能用这种 NewXXXX() 的工厂函数来创建。总之这点我也用得很不爽,明明是面向对象,但用工厂函数写总给人一种自己在写面向过程一样 |
19
vvmint233 2020-08-03 14:54:56 +08:00 1
var r router 只是变量的声明而已, 对于初始化新的结构体我有时候会 new(router).Init("aa"), Init()用来返回原结构体指针, 这样可以链式. 而且已经有 new 这个函数了, 其他类似的关键字会显得的有些多余
另外 gin*Context 的那个, 其实可以自己把 handler 封装一下, 把 if else 压缩在一个函数里面, handler 主体依旧可以符合带返回值函数的样子, 对于 gin 来说, 请求的处理是 steaming 的, 所以没有返回符合框架的设计 |
20
CNife 2020-08-03 15:25:23 +08:00 1
这是 Go 语言设计中比较差的地方。
所有类型都有默认值,但这个默认值不一定有用,甚至可能都不是合法值。 对于 slice,你 var s []int 是合法的,nil 和空 slice 是一回事,append 和 len 也能正确处理 nil 的情况,这时候体验很好。 但对于 map,你 var m map[int]int 就是个非法值,不论是存是取都会炸,这时候 nil 和空 map 就不是一回事了。 根本原因是,每个类型都有自己的初始化方式,它自己最清楚该怎么初始化自己,这就是构造函数的作用。类型可以不定义构造函数,这时候编译器才需要接管类型的初始化工作,给类型一个默认的零值。如果类型自己定义的初始化的方式,编译器就不要越俎代庖了。同时,构造函数是初始化对象的唯一、统一的方法,类型的作者可以由此控制新的对象必定处于合法的初始状态。 但 Go 语言说,没有构造函数了,所有类型都有默认的零值。但有的类型,零值是合法的初始值,但其他类型就未必了。比如这个类型里有指向堆内存的指针,需要在用之前分配内存,nil 肯定不中用了;有的类型, 不过,这也同时取决于类型的设计者有没有考虑到这点,有没有把零值也纳入到合法值的范畴。但这就比较麻烦了,难道要每个函数都要在开头判断一下传进来的是不是 nil,如果是再做个初始化?这可太蠢了,不如直接在文档里规定不允许用 var,必须用 NewXXX 新建对象。 |
21
NCZkevin 2020-08-03 15:30:59 +08:00
go 开发团队喜欢简洁,导致 go 有很多特有的写法,适应了很久才习惯
|
23
neoblackcap 2020-08-03 15:37:58 +08:00
默认的构造函数没有什么用,C++里面要删除各种默认的构造函数的情况还少么?这个真的不是什么黑点,构造函数本质上跟现在这样的静态函数没有什么区别。
|
24
KickAssTonight 2020-08-03 15:38:56 +08:00
主要是因为没有构造方法吧
|
25
Jirajine 2020-08-03 15:47:03 +08:00 via Android 1
@szzhiyang 楼里都在说 constructor 的问题,这个不是 constructor 的问题,而是 go 自作聪明搞得所有变量都默认初始化。
正确的做法是编译器静态检查访问所有对未初始化变量的访问并在编译时报错,而不是给个不正确的初始值然后运行时爆炸。 这种默认初始化值还有一些其他问题,比如 int 传个 0 进来你不知道它是 uninitialized 还是就是 0 。 |
26
icexin 2020-08-03 16:03:44 +08:00
go 的变量初始化就是申请内存,然后 memset 为 0 。如果需要额外的初始化就用一个函数来进行初始化工作,思路很清晰。这一块倒是贯彻了 python 里面的 Explicit is better than implicit 。隐式的构造函数看着很方便,但有时候反而带来了无谓的开销。
|
28
szzhiyang 2020-08-03 16:42:25 +08:00
@Jirajine 也许是因为检查各个变量是否被初始化会让编译期检查变得复杂且缓慢吧,Go 有不少设计就是为了确保编译流程简单且迅速。
|
29
dog82 2020-08-03 16:42:44 +08:00
我写了 2 年多 Go 了,还是对某些语法很排斥。
特别是类型转换,有时会被气爆炸! uint32 不能隐式转成 uint64 |
30
MiskoLee 2020-08-03 16:44:17 +08:00
不知道 LS 一段人在说什么。。。。。。。
这个东西叫做 类型断言。。。。。。,很多现代静态语言都有这个功能,比如 Rust 。 |
31
tairan2006 2020-08-03 17:19:50 +08:00
就是初始化的问题,go 没构造函数其实影响不大
|
32
janxin 2020-08-03 18:02:47 +08:00
@Jirajine 这只是一个 null safety 问题,至于需要确认是否初始化有其他方式可以确定。
在其他语言里也不是什么很特殊的东西。 https://kotlinlang.org/docs/reference/null-safety.html https://dart.dev/null-safety/understanding-null-safety |
33
keepeye 2020-08-03 18:08:25 +08:00
默认值呗
|
34
janxin 2020-08-03 18:10:10 +08:00
关于 null safety,谷歌开发者有一篇文章关于这个问题( Dart ):
https://mp.weixin.qq.com/s/rgVJn928fyGunNO5kDKnSA 但是如果允许 nil 这个特殊类型之后,如果库出现了问题,在没有异常处理的 Go 语言里没处理好可能程序动不动就崩了... |
35
nuk 2020-08-03 18:18:53 +08:00 1
你这里 struct 的 field 都导出了,要是有没导出的咋办?
这个用法基本上和 typedef 一个未声明结构的指针差不多 只能从函数返回,实际上无法实例化 |
36
zxlzy 2020-08-03 19:17:19 +08:00
这不就是构造函数吗
|
37
reus 2020-08-03 20:33:17 +08:00
@Jirajine 连零值的概念都不懂,就别满嘴糟粕糟粕了。map 的零值就是 nil,为啥?如果我只是想声明一个 map 变量,在用现有的 map 赋值,那声明的时候就初始化一个空的干嘛?多余!
|
38
petelin 2020-08-03 20:45:01 +08:00 1
@Jirajine 这就是构造函数啊,难道你们构造东西的时候不写点逻辑进去?
还有,把创建的地方收在一起后期改造也好改啊, 要不然自己去初始化,项目里边 100 多个地方都 create,你想改个默认值都不能改。 唉。。。 |
39
petelin 2020-08-03 20:46:15 +08:00 1
真的忍不住吐槽, 没有基本代码素养的人太多了。。。一个现代的语言动不动被吐槽糟粕。 真正的糟粕是 js 这种历史包袱一大堆的垃圾好吗
|
40
Jirajine 2020-08-03 20:51:21 +08:00 via Android
@reus 你觉得创建出来就是 nil,别人也可以觉得创建出来就得能用,不能是 nil 。
问题就在于它明明可以在编译时检查避免访问未初始化变量,却偏要自作聪明的默认给一个“零值”作为初始值,这个“零值”有时候是想要的有时候不是。 访问 undefined 变量在脚本语言中是很普遍的一类问题,go 作为静态编译型语言完全可以编译时就避免这类问题却不这么做,所以说是糟粕之一。 |
41
Jirajine 2020-08-03 20:54:23 +08:00 via Android
@petelin 我没有说构造函数有问题啊,你到底看没看我写的什么。
我说的是变量默认初始化的问题,换一个正常点的语言楼主这种做法编译时就会得到错误“uninitialized variable”,而 go 的做法使其能够通过编译并在运行时崩溃。 |
42
scnace 2020-08-04 00:38:15 +08:00 via Android 2
|