被 Java 毒害的脑子想在 Go 中实现一个操作,望打醒

6 天前
 assiadamo

以前接触过的一个 Java 项目,实现了一种在我看来很新的做法:

  1. 代码生成的协议类,里面自带了一个未实现的 process 方法
public class Echo extend Msg {
	String msg;
  	public void decode(){}
  	public void encode(){}
  	public void process() throws Exception {
  		throw new UnsupportedOperationException();
  	}
}
  1. 代码生成的协议处理类,格式是这样的
@MsgProcess
public static boolean process(Echo echo) {
	return true;
}
  1. 框架启动的时候,会反射获取到注解@MsgProcess的 Metchod 和他的参数,然后用 javaassist 的字节码操作,将协议类Echoprocess方法给替换掉!这样框架层调用协议的msg.process()就可以直接执行业务逻辑!

Java 写了 10 年,一说起框架,自然想到的就是各种设计模式抽象继承与反射之类,当写 Go 的时候,也受到影响,我现在想用 Go 实现类似的操作,实践的效果如下

  1. 代码生成了 Echo 协议类
package proto
type Echo struct {
	BaseMsg
	Msg string
}
func (msg *Echo) Decode(src *bytes.Buffer) error {}
func (msg *Echo) Encode(dst *bytes.Buffer) error {}
func (msg *Echo) Process() {
	panic("implement me")
}
  1. 代码生成了业务逻辑类
package logic
import proto
func ProcessEcho(msg *proto.Echo) {}
  1. 使用 ast/parser 将Echoprocess的方法体替换为ProcessEcho
func (msg *Echo) Process() {
	logic.ProcessEcho(msg)
}

但重新生成的 Echo 类,有一些问题,首先生成出来的文件,我将其保存为echo_override.go放在另一个 package ,相关的 import 都可能有问题,然后Processimport 了 logic ,而 logic 自然要 import echo ,非常经典的 import cycle 。

这是第一步遇到的问题,我打算先用 interface 解决看看,为什么不用 func 替换,我觉得好丑啊!各位 Go 大神有没有什么建议?我这种思路,符合 Go 的设计哲学吗?

5932 次点击
所在节点    Go 编程语言
68 条回复
Danswerme
5 天前
@assiadamo Vue 这个还是比较好理解的,Vue 2 和 3 的实现原理不太一样,前者是用了 Object.defineProperty ,后者用的是 Proxy ,原理基本都是拦截对 data 的访问,并记录依赖了这个数据的函数,data 数据中发生变化时重新执行依赖中记录的函数就可以更新视图啦。
yoyolichen
5 天前
反射,字节码替换 process ,这操作好骚啊,为什么不用设计模式,比如策略?没细看评论
Keuin
5 天前
首先 go 用 codegen 这个思路没问题,可以看一下 grpc 是怎么做的,利用了 go implements interface implicitly 这个特性,codegen 依赖 service 的 interface ,具体 service 实现留着让人在外部包完成,最后启动的时候组装,也就是形成了单向依赖:
- main 包依赖 protocol 包、service 包
- protocol 和 service 之间不互相依赖( service 隐式实现了 protocol 定义的一个 interface ,但是不需要导入它)
ns09005264
5 天前
看起来似乎你是想给 Msg 的实现类动态指定 process 函数,在 Go 里函数可以作为变量来使用,所以你可以在 Msg 的实现类 Echo 里添加一个字段 Processor ,类型是接口或函数,然后 Echo 的 process 方法内直接固定调用(e *Echo).processor(),具体的 Processor 实现在 NewEcho 注入即可。
和#59 的方式类似,但是 Map[TypeID]Processor 不直观,不如 NewEcho(processor)明确,易于阅读。

还有你 Java 里的那种写法我觉得是旧时代的遗毒,Java 也可以像 Go 那样,将函数作为变量,一切都简单明了,易于追踪。
assiadamo
5 天前
@yoyolichen 策略模式就是类型和他的处理函数的 map ,是用上的,Java 和 Go 的区别在于,Java 可以依赖注解在运行时构建,而 Go 只能手动注册或代码生成,总之要在编译器前准备好,运行时虽然可能可以做到,但性能不一定好
assiadamo
5 天前
@Keuin 我理解了,虽然仍然要在 main 中组装,但基于 interface 的写法更符合 go 的设计一点,我会试试看
james122333
4 天前
确实用 type embedding 组合+interface 就好
毕竟这种东西用常用静态语言实作起来都很难看 註解一样丑的不行 用 shell 类的就会很飘逸
james122333
4 天前
@yolee599

这我说过 想要动态化 一反射 二代码生成
都含不可确定性 代码生成少一些不可确定性

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1095483

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX