go 怎么实现 方法前置操作 类似 PHP 的__call

2022-03-10 12:35:35 +08:00
 dzdh

如 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
    }
}
2854 次点击
所在节点    Go 编程语言
20 条回复
superfatboy
2022-03-10 13:30:53 +08:00
貌似 好像 iris 有一个这个功能,能在路由前置执行,不知道符合不符合你的要求
mybyons
2022-03-10 13:45:05 +08:00
Wrapper / Adapter

```go
func log(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r
*http.Request) {
log.Println("Before")
h.ServeHTTP(w, r) // call original
log.Println("After")
})
}

```
http.Handle("/path", handleThing)
-->
http.Handle("/path", log(handleThing))
mybyons
2022-03-10 13:46:29 +08:00
Wrapper / Adapter

```go
func log(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Before")
h.ServeHTTP(w, r) // call original
log.Println("After")
})
}
```

http.Handle("/path", handleThing)
-->
http.Handle("/path", log(handleThing))
wangritian
2022-03-10 14:35:18 +08:00
go 没有魔术方法,只能自己包一层
james122333
2022-03-10 18:39:20 +08:00
__call 如果没做其他什么直接 type embeding 就可以
有的话只能用反射外包一层 并且方法必须要是 field 不能是 struct 内 method
不然完全找不到 Address...
james122333
2022-03-10 19:00:20 +08:00
可以自己写个 lib 用来初始化 类似 new 或者依赖注入都可以 乍看之下风格是差不多的 type embeding 的状况这些函数 field 也会有
james122333
2022-03-10 19:01:58 +08:00
你要用对象写是会很痛苦的
Goooooos
2022-03-10 19:32:22 +08:00
这不符合 go 的设计思想
dobelee
2022-03-11 01:29:22 +08:00
是要实现类似统一参数校验的效果吗?这种一般包多一层 middleware ,相反 php 的魔术方法才是奇葩实现(相比其他主流)。
lwldcr
2022-03-11 07:42:06 +08:00
Middleware 或者叫拦截器 差不多就这样 把方法作为参数
sampeng
2022-03-11 08:32:27 +08:00
出门右拐用 java ,python ,php ,等灯
MeetTheFuture
2022-03-11 10:07:54 +08:00
Middleware
bugfan
2022-03-11 10:35:21 +08:00
自己包装一层中间件,可以放在主要逻辑前,也可以放在主要逻辑后 类似 https://github.com/bugfan/srv
dzdh
2022-03-11 15:18:40 +08:00
@james122333

就是重写一遍 struct 挨个实现其方法。然后再方法内挨个走我自己的方法再调他原有的方法?

```
type o struct{}
func (_o *o) m1() {
}



type my struct {
o
}
func (_my *my) m1() {
// before
_my.o.m1() // 或者我在 my 里再写个统一的 wrapper ?
// after
}

```

然后 ` xxx.handle(&my{}) ` ?

就是麻烦点。。
james122333
2022-03-11 18:01:36 +08:00
@dzdh

这只是 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")
}
james122333
2022-03-11 18:26:48 +08:00
那个 A 函数只要透过 makefunc 把 call 函数并入一次就可以 比较麻烦的是怎么让一切自动完成
bugfan
2022-03-11 19:28:57 +08:00
@dzdh #14 这种还不算是最优解,其实可以参考一些框架的设计都用了反射,例如:
```

type magic interface {
Call(*some.Class)
}

// 把你的 model 传进去
func Register(m interface{}){
handlers=append(handlers,m)
}

// 在合适的时机执行
func bind(h interface{}){
// 一些逻辑处理 handlers

reflectVal := reflect.ValueOf(h)
t := reflect.Indirect(reflectVal).Type()
newObj := reflect.New(t)
handler, ok := newObj.Interface().(magic)
if ok {
handler.Call(xxxx)
}
// 然后走你 register 进去的 model 的逻辑

}

// 类似 “https://github.com/bugfan/rest/blob/master/rest.go”,不喜轻喷啊😂

```
dzdh
2022-03-11 22:46:39 +08:00
@bugfan

关键是 三方包里 的方法 不是要传入它要求的类型么。这咋搞。是人家的包。不是自己项目里的。自己的项目去调用人家的包。
kongkongyzt
2022-03-12 09:48:14 +08:00
无...
james122333
2022-03-12 20:04:55 +08:00
@dzdh

如果是传入的是 type interface 可以这样做
不然其实只是三方包不希望你这样做
go 本身挺有限制 往好处想其实就是一堆人说的规范

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

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

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

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

© 2021 V2EX