Closed draveness closed 2 years ago
@draveness
很多都很赞同,尤其是纵向划分模块
依赖比较复杂的话可以试下用 wire 来注入
Go 里面搞 monkey patch 感觉很疯狂啊,看原理应该跟 fishhook 差不多
我看了下 wire 感觉还挺有意思的,不过看起来是 alpha 版本
我們是用 https://github.com/uber-go/dig 來解決依賴注入
代码配色的主题叫什么名字?
「过私有的接口体」 应改为「私有的结构体」
可以开通个打赏功能?感觉作者的文章都是用心的干货。
好文章 对 Go 项目工程化很有意义 感谢博主分享
「过私有的接口体」 应改为「私有的结构体」
已经修复了
可以开通个打赏功能?感觉作者的文章都是用心的干货。
暂时不会在博客上挂这个东西..
代码配色的主题叫什么名字?
prism 具体什么名字不记得了
@draveness
很多都很赞同,尤其是纵向划分模块 依赖比较复杂的话可以试下用 wire 来注入 Go 里面搞 monkey patch 感觉很疯狂啊,看原理应该跟 fishhook 差不多
我看了下 wire 感觉还挺有意思的,不过看起来是 alpha 版本
我們是用 https://github.com/uber-go/dig 來解決依賴注入
依赖注入确实是一个比较有意思的话题,但是感觉在 Go 的社区中并不常见
干货十足啊,干过的公司没有写单元测试的,自己想过写但是很多依赖sql和http的不知道怎么搞。
赞,知识量很丰富!
干货十足啊,干过的公司没有写单元测试的,自己想过写但是很多依赖sql和http的不知道怎么搞。
单元测试还是非常重要的 🤣
面向接口部分,NewService返回一个Interface,这是一个很危险的设计。往简单的看,这样导致了返回的struct无法被copy。往复杂了看,这个时候一个interface就变成你的公开接口,这个interface即使只做了向后兼容的改动(例如新增一个method)都要break很多外部代码
面向接口部分,NewService返回一个Interface,这是一个很危险的设计。往简单的看,这样导致了返回的struct无法被copy。往复杂了看,这个时候一个interface就变成你的公开接口,这个interface即使只做了向后兼容的改动(例如新增一个method)都要break很多外部代码
~不返回 interface 难道返回一个 struct 么,那上层依赖的还是 struct,为什么还需要面向接口,NewService 的作用就是返回接口,隐藏内部的实现结构体~
我又查了一下相关的资料,发现关于这个问题,很多人写过文章,大部分的文章都在提到了一句话『Accept interfaces return structs』,也就是接受接口并且返回结构体;而 Dave 曾经也发过一条 Twitter
,我在这里就直接引用一下:
golang top tip: the consumer should define the interface. If you’re defining an interface and an implementation in the same package, you may be doing it wrong.
消费者应该负责定义接口,如果在一个包中同时定义了接口和实现,那么你可能就做错了。
想了一下,这样做在某些情况下确实更好并且合理
struct
就比较有价值,上游可以将返回的结构体方法通过 interface
进行隔离,去掉不会使用的方法,但是这就需要我们谨慎地定义当前结构体的公有方法以及变量;package
单独创建一个 interface
就非常麻烦,我们还是需要在新的 package
中创建 interface
来封装结构体的方式,但是在这种情况下让下游去返回一个 interface
相比之下就更加方便;在一个常见的项目中,使用 NewService
的方式返回一个接口,作者觉得并没有什么问题,无论是 struct
无法被 copy 还是 interface
增加了方法会导致 break 外部的代码(还要有其他人实现这个接口)都不会有太大的影响,很多时候只有返回 interface
才能真正地让别人使用 interface
.
Go 语言官方的 Context 包 就没有使用这种返回结构体的方式,它内部有 emptyCtx
、valueCtx
等私有结构体,但是最后对外暴露的也只有 Context
接口。
在生产者中返回结构体并消费者中定义接口是更加合理的,我们也应该这么去做,但是我也不认为在 NewService
这种『构造器』中返回接口就一定是有问题的。
PS:之后会对文章内容进行修改,非常感谢你提出的问题,让我对这件事情有了更清楚地理解。
面向inteface并不代表所有情况下都用接口。接受接口作为函数参数、返回符合某些接口的struct,也是面向接口。
作者的文章都是干货满满,学习和毅力令人佩服,以后在个人项目中慢慢实践,只是这套用在我目前所在公司的项目中就很难了,公司风气过于浮躁。
看到的晚了😰,已经把项目按照mvc划分了,感觉再改一遍灰常麻烦
图是用什么画的?
接口的例子还有更好的写法:Accept interfaces, return structs
重复的问题,可以看上面的讨论 https://github.com/draveness/blog-comments/issues/149#issuecomment-497902619
醍醐灌顶,获益匪浅
纵向划分模块的情况下,以文中的 post,comment 模型为例,如果我想给 post定义一个 comments 方法(表示post的所有comments),给comment定义一个post方法(表示comment所属post),但是这不就造成循环引用了吗?有什么办法吗?
纵向划分模块的情况下,以文中的 post,comment 模型为例,如果我想给 post定义一个 comments 方法(表示post的所有comments),给comment定义一个post方法(表示comment所属post),但是这不就造成循环引用了吗?有什么办法吗?
文章举得例子可能不是特别恰当,想要说明的是我们要按照职责进行拆分,并不一定要分的这么细,我们还是要选择一个合适的粒度。
如果遇到了这种情况你说的这种情况,其实在设计上就对两个模块约定了较强的耦合,在这时如果:
package
合起来吧,blog
更像是一个合适的拆分粒度;接触了一个go项目,一个源文件引入十几个包,很多包都有 init(), 看源文件代码的时候有些乱,一个变量没有初始化怎么使用的,最后发现是在 init() 中做的。 类似操作很多,导致看代码的时候不得不先关注引入包的init()执行了哪些操作,增加了理解代码的难度。
@SmileEye 接触了一个go项目,一个源文件引入十几个包,很多包都有 init(), 看源文件代码的时候有些乱,一个变量没有初始化怎么使用的,最后发现是在 init() 中做的。 类似操作很多,导致看代码的时候不得不先关注引入包的init()执行了哪些操作,增加了理解代码的难度。
所以应该尽量避免这种使用方式,如果需要init初始化,最好每个包独立一个init文件用来存放init相关的操作。
gomock部分,那么当我在不测试的情况下,要new一个service时的Blog接口参数如何而来呢?&jekyll或者 &wordpress 嘛?
gomock部分,那么当我在不测试的情况下,要new一个service时的Blog接口参数如何而来呢?&jekyll或者 &wordpress 嘛?
没明白你的问题,能说的详细点么?
@draveness
gomock部分,那么当我在不测试的情况下,要new一个service时的Blog接口参数如何而来呢?&jekyll或者 &wordpress 嘛?
没明白你的问题,能说的详细点么?
我才刚学go没多久,基础不够扎实,有些问题比较没水平,请见谅。我上一个问题想要描述的是:在生产环境时,我们要实例化一个service出来,然后调用该实例的ListPosts()方法获取文章。问题就是实例化的时候NewService(Blog)里的Blog接口参数该怎么制造出来呢?我搜索了资料,接口变量存储的是实现者的值,所以我想确认的是 jekyll、wordpress结构体实现了Blog接口,那么他们的具体值(实例化后)即可当做形参Blog的值传输进去吧?
@draveness
gomock部分,那么当我在不测试的情况下,要new一个service时的Blog接口参数如何而来呢?&jekyll或者 &wordpress 嘛?
没明白你的问题,能说的详细点么?
我才刚学go没多久,基础不够扎实,有些问题比较没水平,请见谅。我上一个问题想要描述的是:在生产环境时,我们要实例化一个service出来,然后调用该实例的ListPosts()方法获取文章。问题就是实例化的时候NewService(Blog)里的Blog接口参数该怎么制造出来呢?我搜索了资料,接口变量存储的是实现者的值,所以我想确认的是 jekyll、wordpress结构体实现了Blog接口,那么他们的具体值(实例化后)即可当做形参Blog的值传输进去吧?
我们其实有两种选择:
一般情况可以这样,在 jekyll
和 wordpress
各自的 package 里面创建一个 NewBlog
方法返回 Blog
接口
// pkg/jekyll/blog.go
package jekyll
type jekyll struct{}
func NewBlog(xxx) blog.Blog {
return jekyll{}
}
func (j *jekyll) ListPosts() {}
// pkg/wordpress/blog.go
package wordpress
type wordpress struct{}
func NewBlog(xxx) blog.Blog {
return &wordpress{}
}
func (j *wordpress) ListPosts() {}
也可以返回结构体,就像在 这里 讨论的,这些结构体需要实现 ListPosts
方法,只要实现了 ListPosts
方法就意味着当前结构体可以作为参数传入 NewService(blog.Blog)
// pkg/jekyll/blog.go
package jekyll
type Blog struct {
}
func NewBlog(xxx) Blog {
return Blog{}
}
func (*Blog) ListPosts() {}
New
方法会返回接口,当前包会依赖其他 package
中定义的接口;New
方法会返回结构体或者结构体指针;这两种方式怎么选就看你自己了,我一般在普通的业务服务中会用第一种方式,在框架和库中会用第二种
非常感谢楼主,解答了我的疑惑的同时还提供了新的思路,由于上条回复过长就不再上条回复进行回复了,以后会多拜读楼主的博客的
你好看了你的文章很受启发。我最近想参考一些go语言开源项目学习go语言使用方法,有什么好的项目推荐么?
文章写得真棒👍
学到很多东西,受启发了,谢谢
我觉得自己这活到狗身上去了。。。佩服,向作者学习
"每一个单元测试都表示一个可能发生的情况,单元测试就是业务逻辑。" 这句话说到我心坎里去了
"每一个单元测试都表示一个可能发生的情况,单元测试就是业务逻辑。" 这句话说到我心坎里去了
还行是吧,哈哈哈
题主 你好 有没有哪个项目比较符合你这篇的最佳实践 我刚在写第一个go项目 刚好可以借鉴下~
题主 你好 有没有哪个项目比较符合你这篇的最佳实践 我刚在写第一个go项目 刚好可以借鉴下~
你可以看看 prometheus、kubernetes 这些开源项目,从目录结构开始,多用工具找找感觉
单元测试使用这个gomonkey使用感觉还不错 https://github.com/agiledragon/gomonkey
单元测试使用这个gomonkey使用感觉还不错 https://github.com/agiledragon/gomonkey
文档看起来不是特别全,例子都在单元测试里
请问移动端开发单元测试也是必要的么? 另外移动端单元测试的话, 还需要覆盖到接口的测试么?
请问移动端开发单元测试也是必要的么? 另外移动端单元测试的话, 还需要覆盖到接口的测试么?
那还是看看投入产出比吧,如果是界面单元测试,个人觉得没有必要,不过框架或者库还是需要写的,
@draveness
请问移动端开发单元测试也是必要的么? 另外移动端单元测试的话, 还需要覆盖到接口的测试么?
那还是看看投入产出比吧,如果是界面单元测试,个人觉得没有必要,不过框架或者库还是需要写的,
感谢感谢, 真的解惑了 :)
赞
谢谢.
您好 我有个问题关于拆分接口哪里想请问一下 您说的当一New的时候返回一个接口类型 那么如果我这个结构体只有这么一个且不会扩展的情况下是否还需要返回接口类型呢
您好 我有个问题关于拆分接口哪里想请问一下 您说的当一New的时候返回一个接口类型 那么如果我这个结构体只有这么一个且不会扩展的情况下是否还需要返回接口类型呢
这种情况都可以,在项目中统一就行
纵向切分真的是醍醐灌顶啊,最近刚开始从头写一个玩具后端,用MVC最后代码里有好多同名的包,写的好难受~谢谢博主
感谢分享,很棒文章
复杂业务service 层与repo层是否也需要用接口拆分,如果垂直拆分对于大一些的项目,颗粒度还是有点粗了
https://draveness.me/golang-101