Как добавить новые методы к существующему типу в go?


Я хочу добавить удобный метод util на маршрут gorilla/mux и типы маршрутизаторов:

package util

import(
    "net/http"
    "github.com/0xor1/gorillaseed/src/server/lib/mux"
)

func (r *mux.Route) Subroute(tpl string, h http.Handler) *mux.Route{
    return r.PathPrefix("/" + tpl).Subrouter().PathPrefix("/").Handler(h)
}

func (r *mux.Router) Subroute(tpl string, h http.Handler) *mux.Route{
    return r.PathPrefix("/" + tpl).Subrouter().PathPrefix("/").Handler(h)
}

но компилятор сообщает мне

Не удается определить новые методы для нелокального типа mux.Маршрутизатор

Так как бы я этого достиг? Я создаю новый тип структуры, который имеет анонимный mux.Маршрут и мультиплексирования.Поля маршрутизатора? Или что-то еще?

2 69

2 ответа:

как упоминает компилятор, вы не можете расширить существующие типы в другом пакете. Вы можете определить свой собственный псевдоним или подпакет следующим образом:

type MyRouter mux.Router

func (m *MyRouter) F() { ... }

или путем встраивания оригинального маршрутизатора:

type MyRouter struct {
    *mux.Router
}

func (m *MyRouter) F() { ... }

...
r := &MyRouter{router}
r.F()

Я хотел бы расширить ответ, данный @jimt здесь. Этот ответ правильный и очень помог мне разобраться в этом. Тем не менее, есть некоторые предостережения к обоим методам (alias, embed), с которыми у меня были проблемы.

Примечание: Я использую термины родитель и ребенок, хотя я не уверен, что это лучший для композиции. По сути, родитель-это тип, который вы хотите изменить локально. Ребенок-это новый тип, который пытается реализовать это модификация.

Метод 1 - Псевдоним

type child parent
// or
type MyThing imported.Thing
  • обеспечивает доступ к полям.
  • не предоставляет доступ к методам.

Метод 2-Вложение (официальная документация)

type child struct {
    parent
}
// or with import and pointer
type MyThing struct {
    *imported.Thing
}
  • обеспечивает доступ к полям.
  • предоставляет доступ к методам.
  • необходимые для рассмотрения инициализация.

резюме

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

вы можете увидеть это в следующем коде.

рабочий пример на детской площадке

package main

import (
    "fmt"
)

type parent struct {
    attr string
}

type childAlias parent

type childObjParent struct {
    parent
}

type childPointerParent struct {
    *parent
}

func (p *parent) parentDo(s string) { fmt.Println(s) }
func (c *childAlias) childAliasDo(s string) { fmt.Println(s) }
func (c *childObjParent) childObjParentDo(s string) { fmt.Println(s) }
func (c *childPointerParent) childPointerParentDo(s string) { fmt.Println(s) }

func main() {
    p := &parent{"pAttr"}
    c1 := &childAlias{"cAliasAttr"}
    c2 := &childObjParent{}
    // When the parent is a pointer it must be initialized.
    // Otherwise, we get a nil pointer error when trying to set the attr.
    c3 := &childPointerParent{}
    c4 := &childPointerParent{&parent{}}

    c2.attr = "cObjParentAttr"
    // c3.attr = "cPointerParentAttr" // NOGO nil pointer dereference
    c4.attr = "cPointerParentAttr"

    // CAN do because we inherit parent's fields
    fmt.Println(p.attr)
    fmt.Println(c1.attr)
    fmt.Println(c2.attr)
    fmt.Println(c4.attr)

    p.parentDo("called parentDo on parent")
    c1.childAliasDo("called childAliasDo on ChildAlias")
    c2.childObjParentDo("called childObjParentDo on ChildObjParent")
    c3.childPointerParentDo("called childPointerParentDo on ChildPointerParent")
    c4.childPointerParentDo("called childPointerParentDo on ChildPointerParent")

    // CANNOT do because we don't inherit parent's methods
    // c1.parentDo("called parentDo on childAlias") // NOGO c1.parentDo undefined

    // CAN do because we inherit the parent's methods
    c2.parentDo("called parentDo on childObjParent")
    c3.parentDo("called parentDo on childPointerParent")
    c4.parentDo("called parentDo on childPointerParent")
}