Как определить, что базовая функция типа была переопределена в Go, без вызова функции? - PullRequest
4 голосов
/ 17 марта 2019

Я реализую простой роутер в Go. Раньше у меня было много избыточного кода для каждой конечной точки, возвращавшего ошибку, когда вызванный метод не был реализован для этой конечной точки. Я произвел рефакторинг и создал «базовый» тип, который предоставляет функции по умолчанию для каждого типа запроса, которые просто возвращают невыполненную ошибку. Теперь все, что мне нужно сделать, это переопределить определенные функции метода для данной конечной точки, которые я хочу реализовать. Это было весело и забавно, пока я не хотел выяснить, учитывая переменную конечной точки, какой из методов был переопределен?

Пропуск посторонних деталей, вот такой простой пример, какой я могу себе представить сейчас:

package main

import (
    "fmt"
)

// Route defines the HTTP method handlers.
type Route interface {
    Get() string
    Post() string
}

// BaseRoute is the "fallback" handlers,
// if those handlers aren't defined later.
type BaseRoute struct{}

func (BaseRoute) Get() string {
    return "base get"
}

func (BaseRoute) Post() string {
    return "base post"
}

// Endpoint holds a route for handling the HTTP request,
// and some other metadata related to that request.
type Endpoint struct {
    BaseRoute
    URI string
}

// myEndpoint is an example endpoint implementation
// which only implements a GET request.
type myEndpoint Endpoint

func (myEndpoint) Get() string {
    return "myEndpoint get"
}

func main() {
    myEndpointInstance := myEndpoint{URI: "/myEndpoint"}
    fmt.Println(myEndpointInstance.URI)
    fmt.Println(myEndpointInstance.Get())
    fmt.Println(myEndpointInstance.Post())
}

Этот фрагмент выведет следующее:

/myEndpoint
myEndpoint get
base post

Таким образом, мое переопределение функций работает как задумано. Теперь мне интересно, что в моей основной функции, после того как я объявил myEndpointInstance, могу ли я как-то сказать, что функция Post не была переопределена и все еще реализуется базовым BaseRoute без фактического вызова функции? В идеале я хочу что-то вроде этого:

func main() {
    myEndpointInstance := myEndpoint{URI: "/myEndpoint"}
    if myEndpointInstace.Post != BaseRoute.Post {
        // do something
    }
}

Я немного поиграл с пакетом отражения, но не нашел ничего полезного.

1 Ответ

5 голосов
/ 17 марта 2019

Как отмечают другие, какой метод вызывать, это решение во время компиляции.Таким образом, вы можете проверить это во время компиляции, большинство IDE будут ориентировать вас на метод, связанный с фактическим вызовом.

Если вы хотите проверить это во время выполнения, вы можете сравнить указатели функций.Вы не можете сравнивать значения функций, они не сопоставимы (только со значением nil). Spec: Операторы сравнения :

Значения среза, карты и функции не сравнимы.Однако в особом случае значение среза, карты или функции можно сравнить с предварительно объявленным идентификатором nil.

. Это можно сделать следующим образом:

myEndpointInstance := myEndpoint{URI: "/myEndpoint"}

v1 := reflect.ValueOf(myEndpointInstance.Post).Pointer()
v2 := reflect.ValueOf(myEndpointInstance.BaseRoute.Post).Pointer()
fmt.Println(v1, v2, v1 == v2)

v1 = reflect.ValueOf(myEndpointInstance.Get).Pointer()
v2 = reflect.ValueOf(myEndpointInstance.BaseRoute.Get).Pointer()
fmt.Println(v1, v2, v1 == v2)

Будет выведено (попробуйте на Go Playground ):

882848 882848 true
882880 882912 false

Вывод сообщает, что Post() не "переопределен" (myEndpointInstance.Post совпадает с myEndpointInstance.BaseRoute.Post), в то время как Get() - это (myEndpointInstance.Get не совпадает с myEndpointInstance.BaseRoute.Get).

См. связанные вопросы:

Как сравнить 2 функции в Go?

Коллекция уникальных функций в Go

...