Насмешливые сложные методы для структур, используемых в void-функции - PullRequest
0 голосов
/ 25 августа 2018

Моя проблема не очень сложная. Сначала я напишу простой пример, а затем покажу реальный код на случай, если пропущу какие-либо тонкости.

Итак, у меня есть сторонняя библиотека:

// Third party library
type ThirdPartyFoo struct {}    
func (t *ThirdPartyFoo) DoFoo () *ThirdPartyFoo {
    println("I'm a third party library!")
    return t
}

И я хочу использовать эту библиотеку для вызова DoFoo():

type MyThing struct {
    TheFoo ThirdPartyFoo
}

func (myThing *MyThing) WhichLib () {
    myThing.TheFoo.DoFoo()
}

И я хочу протестировать его с поддельной версией ThirdPartyFoo, потому что, как еще я должен проверить, что DoFoo() был вызван A), B) вызван с правильными параметрами, и C) MyThing отвечает правильно на основе различных возвращаемых значений?

Я пытался использовать интерфейс внутри MyThing вместо того, чтобы напрямую зависеть от ThirdPartyFoo ...

type IThirdPartyFoo interface {
    DoFoo() *IThirdPartyFoo
}

type MyThingWithI struct {
    TheFoo IThirdPartyFoo
}

func (myThing *MyThingWithI) WhichLib () {
    myThing.TheFoo.DoFoo()
}

А затем вызывать функцию ...

func main() {
    realFoo := ThirdPartyFoo{}
    realThing := MyThingWithI{
        TheFoo: &realFoo,
    }
    realThing.WhichLib()
}

Но во время выполнения происходит сбой:

proxyutils/serviceregistrar.go:44:9: cannot use &realFoo (type *ThirdPartyFoo) as type IThirdPartyFoo in field value:
        *ThirdPartyFoo does not implement IThirdPartyFoo (wrong type for DoFoo method)
                have DoFoo() *ThirdPartyFoo
                want DoFoo() IThirdPartyFoo

Итак ... Я потратил пару часов, пытаясь выяснить это, и еще час, написав этот пост ... и я все еще потерян. Единственное решение, которое я могу придумать, - это полностью обернуть стороннюю библиотеку, вот так:

type IThirdPartyFooWrapper interface {
    DoFoo() IThirdPartyFooWrapper
}

type ThirdPartyFooWrapper struct {
    IThirdPartyFooWrapper

    ThirdPartyFoo ThirdPartyFoo
}

func (w *ThirdPartyFooWrapper) DoFoo () IThirdPartyFooWrapper {
    w.ThirdPartyFoo.DoFoo()
    return w
}

type MyThingWithI struct {
    TheFooWrapper IThirdPartyFooWrapper
}

func (myThing *MyThingWithI) WhichLib() {
    myThing.TheFooWrapper.DoFoo()
}

Но уже есть ОЧЕНЬ МНОЖЕСТВО уровней косвенности, которые я очень не хочу добавлять этот. Это то, для чего было сделано насмешливо, но кажется, что язык Go полностью облажал любого несчастного чмока, достаточно неудачного для того, чтобы начать использовать стороннюю библиотеку, которая не использует интерфейсы.

И, наконец, вот мой настоящий код. Это *mux.Router как часть ServiceRegistrar, которая является проблематичной.

package proxyutils

import (
    "errors"
    "github.com/gorilla/mux"
    "net/http"
)

type IProxyRegistrar interface {
    Register(input Registration) error
}

type ServiceRegistrarFactory struct{}

func (_ *ServiceRegistrarFactory) Build(proxyRegistrar IProxyRegistrar, router *mux.Router) (*ServiceRegistrar, error) {
    if nil == proxyRegistrar {
        return nil, errors.New("cannot create ServiceRegistrar with nil IProxyRegistrar")
    }
    if nil == router {
        return nil, errors.New("cannot create ServiceRegistrar with nil mux.Router")
    }
    return &ServiceRegistrar{
        proxyRegistrar: proxyRegistrar,
        router:         router,
    }, nil
}

type ServiceRegistrar struct {
    proxyRegistrar IProxyRegistrar
    router         *mux.Router
}

func (r *ServiceRegistrar) Register(userRegistration Registration, f func(http.ResponseWriter, *http.Request)) error {
    err := r.proxyRegistrar.Register(userRegistration)
    if nil == err {
        var route *mux.Route
        if userRegistration.PathIsPrefix {
            route = r.router.PathPrefix(userRegistration.Path)
        } else {
            route = r.router.Path(userRegistration.Path)
        }
        route.Methods(userRegistration.Methods...).HandlerFunc(f)
        return nil
    } else {
        return err
    }
}
...