go 程序结构的探讨 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
yujianwjj
V2EX    Go 编程语言

go 程序结构的探讨

  yujianwjj 344 天前 2226 次点击
这是一个创建于 344 天前的主题,其中的信息可能已经有所发展或是发生改变。

一个项目就是几个模块的组合,假设有 3 个模块,目前我看到以下几种架构:

type InterfaceA interface { MethodA1() MethodA2() } type structA struct { a int } func (a *structA)MedhtodA1() {} func (a *structA)MedhtodA2() {} func (a *structA)run(context) { for { // MethodA1() 和 MethodA2() 的一些调用 } } func NewStructA() structA { a := structA{} go a.run() return a } // 类似的还有 InterfaceB/structB ,特点都是在 New 方法里面开了一个 goroutine ,运行 run 方法。 type InterfaceC interface { MethodC1() MethodC2() MethodC3() } // 这里 c 的运行需要 a type structC struct { a struct c string } func (c *structC)MedhtodC1() {} func (c *structC)MedhtodC2() {} func (c *structC)MedhtodC3() {} func (a *structA)run(context) { for { // MethodC1()、MethodC2()、MethodC3()及 MethodA()的一些调用 } } func NewStructC() InterfaceC { a := structA{} c := structC{ a } go c.run() return c } type manager struct { ib InterfaceB ic InterfaceC } func NewManager() *manager{ ia := NewStructA() ib := NewStructB() ic := NewStructC(ia) return &manager{ ib ic } } func (m *manager)run(ch) { for { select { case <- ch: return } } } func main() { m := NewManager() m.run() } 

这种架构的话,很清晰的表明了程序模块,比如直接从项目的目录结构就可以看到这个程序的几个模块(一个模块对应一个目录),每个模块的接口功能表达也很清晰。但是他的问题是,在阅读代码的时候,隐藏了程序里面的几个模块的执行和交互逻辑。比如我从 main 看到 manager 的时候,再看到 manager.run 方法就结束了,完全不知道程序内部的模块是怎么运行。

// 另外一种架构 type Module interface { Run() } type StructA struct { } func (a *StructA) Run() { a.methodA1() a.methodA2() } type StructB struct { } func (a *StructB) Run() { b.methodB1() b.methodB2() } type StructC struct { a StructA } func (a *StructC) Run() { a.Run() c.MethodC1() c.MethodC2() } type manager struct { b StructB c StructC } func NewManager() *manager{ a := NewStructA() b := NewStructB() c := NewStructC(a) return &manager{ b, c } } func (m *manager)run(ch) { go m.b.Run() go m.c.Run() for { select { case <- ch: return } } } func main() { m := NewManager() m.run() } 

这样的话,阅读代码的时候,可以快速的知道程序的执行逻辑。但是吧,每个模块对外只有一个 Run 方法,模块本身的含义又没了。

这里,估计有人会提出下面的方案。

type Stage interface { Run() } type InterfaceA interface { Stage MethodA1() MethodA2() } type InterfaceB interface { Stage MethodB1() MethodB2() } type InterfaceC interface { Stage MethodC1() MethodC2() MethodC3() } 

这个也是可行的,但是吧,看着别扭,因为这个 Run() 其实跟接口自身的含义没啥关系。

想问问大家,怎么设计程序的模块架构的。

5 条回复    2024-11-02 00:07:26 +08:00
3IOhG7M0knRu5UlC
    1
3IOhG7M0knRu5UlC  
   344 天前 via Android
不明白第一个问题,你的外层 run 起来后监听信号不就结束了吗 看代码到服务里去看
yujianwjj
    2
yujianwjj  
OP
   344 天前 via Android
run 是小写,外层调用不了
neotheone2333
    3
neotheone2333  
   343 天前
OP 的意思是,struct 的属性类型指定 interface 还是 struct 的问题?

如果我理解的没错的话,我的习惯是:
1. 属性类型用 interface
2. NewXXX 方法接受参数类型用 interface ,返回值用 struct
3. 看 interface 的实现用 IDE 的 go to implementation 功能,具体哪走的一个实现 debug 打断点看
reatang
    4
reatang  
   343 天前
interface 主要用在:
1 、可替换实现的地方
2 、会产生包依赖冲突的地方

其他情况下,不建议使用 interface
CLMan
    5
CLMan  
   343 天前
模式 1 ,定义了一堆 interface ,模块执行逻辑在 New 里面,间接在 run 里面。
模式 2 ,定义了一个通用的 Module 接口,模块实现 Module 接口,模块执行逻辑在 Run ,由 Manager 在 run 里面调用。
模式 3 ,定义了一个通用接口 Stage ,以及定义了一堆 interface ,模块实现 Stage 接口以及自身的接口,模块执行逻辑在 Run ,由 Manager 在 run 里面调用。

所以你这 3 种模式,无非就是抽取了一堆接口,有点 Java 了,代码的可读性没有任何区别。

首先,模式 1 的将模块执行触发丢在 New 里面,通常是不建议的,因为多模块系统是由模块组装得到的,最后再走启动的流程,模块提前执行是不合适的。

其次,除非真有必要,Go 是很少上来就抽取一堆 interface ,建议先写下原型,根据实际需要再抽取接口。
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3402 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 22ms UTC 00:40 PVG 08:40 LAX 17:40 JFK 20:40
Do have faith in what you're doing.
ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86