The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
dzdh

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

  •  
  •   dzdh · Mar 10, 2022 · 3339 views
    This topic created in 1537 days ago, the information mentioned may be changed or developed.

    如 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
        }
    }
    
    20 replies    2022-03-12 20:04:55 +08:00
    superfatboy
        1
    superfatboy  
       Mar 10, 2022
    貌似 好像 iris 有一个这个功能,能在路由前置执行,不知道符合不符合你的要求
    mybyons
        2
    mybyons  
       Mar 10, 2022
    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
        3
    mybyons  
       Mar 10, 2022
    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
        4
    wangritian  
       Mar 10, 2022
    go 没有魔术方法,只能自己包一层
    james122333
        5
    james122333  
       Mar 10, 2022
    __call 如果没做其他什么直接 type embeding 就可以
    有的话只能用反射外包一层 并且方法必须要是 field 不能是 struct 内 method
    不然完全找不到 Address...
    james122333
        6
    james122333  
       Mar 10, 2022
    可以自己写个 lib 用来初始化 类似 new 或者依赖注入都可以 乍看之下风格是差不多的 type embeding 的状况这些函数 field 也会有
    james122333
        7
    james122333  
       Mar 10, 2022
    你要用对象写是会很痛苦的
    Sezxy
        8
    Sezxy  
       Mar 10, 2022 via Android
    这不符合 go 的设计思想
    dobelee
        9
    dobelee  
       Mar 11, 2022
    是要实现类似统一参数校验的效果吗?这种一般包多一层 middleware ,相反 php 的魔术方法才是奇葩实现(相比其他主流)。
    lwldcr
        10
    lwldcr  
       Mar 11, 2022 via iPhone
    Middleware 或者叫拦截器 差不多就这样 把方法作为参数
    sampeng
        11
    sampeng  
       Mar 11, 2022 via iPhone
    出门右拐用 java ,python ,php ,等灯
    MeetTheFuture
        12
    MeetTheFuture  
       Mar 11, 2022
    Middleware
    bugfan
        13
    bugfan  
       Mar 11, 2022
    自己包装一层中间件,可以放在主要逻辑前,也可以放在主要逻辑后 类似 https://github.com/bugfan/srv
    dzdh
        14
    dzdh  
    OP
       Mar 11, 2022
    @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
        15
    james122333  
       Mar 11, 2022
    @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
        16
    james122333  
       Mar 11, 2022 via Android
    那个 A 函数只要透过 makefunc 把 call 函数并入一次就可以 比较麻烦的是怎么让一切自动完成
    bugfan
        17
    bugfan  
       Mar 11, 2022
    @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
        18
    dzdh  
    OP
       Mar 11, 2022
    @bugfan

    关键是 三方包里 的方法 不是要传入它要求的类型么。这咋搞。是人家的包。不是自己项目里的。自己的项目去调用人家的包。
    kongkongyzt
        19
    kongkongyzt  
       Mar 12, 2022
    无...
    james122333
        20
    james122333  
       Mar 12, 2022 via Android
    @dzdh

    如果是传入的是 type interface 可以这样做
    不然其实只是三方包不希望你这样做
    go 本身挺有限制 往好处想其实就是一堆人说的规范
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5537 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 76ms · UTC 08:47 · PVG 16:47 · LAX 01:47 · JFK 04:47
    ♥ Do have faith in what you're doing.