Golang Go语言中怎么实现方法前置操作 类似 PHP 的__call
如 php:
class a
{
public function __call($method, $args) {
return call_user_func([new b(), $method], $args);
}
}
class b {}
不能是 xx.call('method',...)
最好还是 xx.method
场景是 三方包 不想改,三方包里要 xxx(handler{})
然后 handler 里有一些方法。想实现在三方包调用 handler.xx 的时候先经过一次自己的验证。
否则 handler 可能就要写成
func (h *handler) m1 {
if h.xx == nil {
// xx
}
}
func (h *handler) m2 {
if h.xx == nil {
// xx
}
}
func (h *handler) m3 {
if h.xx == nil {
// xx
}
}
Golang Go语言中怎么实现方法前置操作 类似 PHP 的__call
更多关于Golang Go语言中怎么实现方法前置操作 类似 PHP 的__call的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
貌似 好像 iris 有一个这个功能,能在路由前置执行,不知道符合不符合你的要求
更多关于Golang Go语言中怎么实现方法前置操作 类似 PHP 的__call的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Wrapper / Adapter go<br>func log(h http.Handler) http.Handler {<br> return http.HandlerFunc(func(w http.ResponseWriter, r <br> *http.Request) {<br> log.Println("Before")<br> h.ServeHTTP(w, r) // call original<br> log.Println("After")<br> })<br>}<br><br>
http.Handle("/path", handleThing)
–>
http.Handle("/path", log(handleThing))
Wrapper / Adaptergo<br>func log(h http.Handler) http.Handler {<br> return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {<br> log.Println("Before")<br> h.ServeHTTP(w, r) // call original<br> log.Println("After")<br> })<br>}<br>
http.Handle("/path", handleThing)
–>
http.Handle("/path", log(handleThing))
go 没有魔术方法,只能自己包一层
__call 如果没做其他什么直接 type embeding 就可以
有的话只能用反射外包一层 并且方法必须要是 field 不能是 struct 内 method
不然完全找不到 Address…
可以自己写个 lib 用来初始化 类似 new 或者依赖注入都可以 乍看之下风格是差不多的 type embeding 的状况这些函数 field 也会有
你要用对象写是会很痛苦的
这不符合 go 的设计思想
是要实现类似统一参数校验的效果吗?这种一般包多一层 middleware ,相反 php 的魔术方法才是奇葩实现(相比其他主流)。
Middleware 或者叫拦截器 差不多就这样 把方法作为参数
出门右拐用 java ,python ,php ,等灯
Middleware
自己包装一层中间件,可以放在主要逻辑前,也可以放在主要逻辑后 类似 https://github.com/bugfan/srv
就是重写一遍 struct 挨个实现其方法。然后再方法内挨个走我自己的方法再调他原有的方法?<br>type o struct{}<br>func (_o *o) m1() {<br>}<br><br><br><br>type my struct {<br> o<br>}<br>func (_my *my) m1() {<br> // before<br> _my.o.m1() // 或者我在 my 里再写个统一的 wrapper ?<br> // after<br>}<br><br>
然后 xxx.handle(&my{})
?
就是麻烦点。。
这只是 type embedding 如果要实现你要的风格 并且有其他动作 就得替换方法包一层 每个都要呼叫魔术方法
另外反射讲的是写个 library 把下列类似代码串起来(反射再包一层的方法是 reflect.MakeFunc) 然后如何初始化 struct 内 func 自己想想 毕竟每次 new 一个出来还要指定哪个 func 很麻烦 所以才会有后面那个bind
主要是透过 reflect.MakeFunc 替换原来 func field 指定先做魔术方法再做原来的方法
type Test struct {
Call func(t *Test) bind:"Call"
A func(t *Test) bind:"A"
}
var Call = func(t *Test) {
fmt.Println(“Call”)
}
var A = func(t *Test) {
fmt.Println(“A”)
}
那个 A 函数只要透过 makefunc 把 call 函数并入一次就可以 比较麻烦的是怎么让一切自动完成
#14 这种还不算是最优解,其实可以参考一些框架的设计都用了反射,例如:<br><br>type magic interface {<br> Call(*some.Class)<br>}<br><br>// 把你的 model 传进去<br>func Register(m interface{}){<br> handlers=append(handlers,m)<br>}<br><br>// 在合适的时机执行<br>func bind(h interface{}){<br>// 一些逻辑处理 handlers<br><br> reflectVal := reflect.ValueOf(h)<br> t := reflect.Indirect(reflectVal).Type()<br> newObj := reflect.New(t)<br> handler, ok := newObj.Interface().(magic)<br> if ok {<br> handler.Call(xxxx)<br> }<br>// 然后走你 register 进去的 model 的逻辑<br><br>}<br><br>// 类似 “<a target="_blank" href="https://github.com/bugfan/rest/blob/master/rest.go”,不喜轻喷啊😂" rel="nofollow noopener">https://github.com/bugfan/rest/blob/master/rest.go”,不喜轻喷啊😂</a><br><br>
关键是 三方包里 的方法 不是要传入它要求的类型么。这咋搞。是人家的包。不是自己项目里的。自己的项目去调用人家的包。
无…
如果是传入的是 type interface 可以这样做
不然其实只是三方包不希望你这样做
go 本身挺有限制 往好处想其实就是一堆人说的规范
在 Go 语言中,并不像 PHP 那样直接支持 __call
这样的魔术方法来实现方法前置操作。不过,你可以通过一些设计模式或技术手段来模拟类似的行为。
一种常见的方法是使用结构体嵌套和接口。你可以定义一个基础结构体,其中包含前置操作的逻辑,然后在该结构体中嵌套一个实际执行操作的结构体。通过方法重写或接口实现,你可以在执行实际方法之前调用前置操作。
例如:
type Base struct {
// 嵌套的实际结构体
RealStruct
}
// 前置操作
func (b *Base) PreCall() {
fmt.Println("前置操作执行")
}
// 调用实际方法前执行前置操作
func (b *Base) CallMethod(name string, args ...interface{}) (results []reflect.Value, err error) {
b.PreCall()
method := reflect.ValueOf(b.RealStruct).MethodByName(name)
if !method.IsValid() {
return nil, fmt.Errorf("方法 %s 不存在", name)
}
return method.Call(args), nil
}
// 使用时,通过 Base 结构体调用方法
base := &Base{RealStruct: Real{}}
results, err := base.CallMethod("ActualMethod", arg1, arg2)
注意,这种方法使用了反射(reflect
包),它允许在运行时检查类型和调用方法,但反射通常会有一定的性能开销。因此,在实际应用中,需要根据性能需求谨慎使用。