web-dev-qa-db-fra.com

Comment ajouter de nouvelles méthodes à un type existant dans Go?

Je veux ajouter une méthode de commodité util sur gorilla/mux Types de route et de routeur:

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)
}

mais le compilateur me dit

Impossible de définir de nouvelles méthodes sur le type non local mux.Router

Alors, comment pourrais-je y parvenir? Est-ce que je crée un nouveau type de structure avec des champs anonymes mux.Route et mux.Router? Ou autre chose?

96
Daniel Robinson

Comme le compilateur le mentionne, vous ne pouvez pas étendre les types existants dans un autre package. Vous pouvez définir votre propre alias ou sous-package comme suit:

type MyRouter mux.Router

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

ou en intégrant le routeur d'origine:

type MyRouter struct {
    *mux.Router
}

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

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

Je voulais développer la réponse donnée par @jimt ici . Cette réponse est correcte et m'a considérablement aidé à résoudre ce problème. Cependant, il y a quelques réserves aux deux méthodes (alias, embed) avec lesquelles j'ai eu du mal.

note : J'utilise les termes parent et enfant, bien que je ne sois pas sûr que ce soit le meilleur pour la composition. Fondamentalement, parent est le type que vous souhaitez modifier localement. Enfant est le nouveau type qui tente d'implémenter cette modification.

Méthode 1 - Définition du type

type child parent
// or
type MyThing imported.Thing
  • Donne accès aux champs.
  • Ne donne pas accès aux méthodes.

Méthode 2 - Enrobage ( documentation officielle )

type child struct {
    parent
}
// or with import and pointer
type MyThing struct {
    *imported.Thing
}
  • Donne accès aux champs.
  • Fournit un accès aux méthodes.
  • Nécessite une considération pour l'initialisation.

Sommaire

  • À l'aide de la méthode de composition, le parent incorporé ne sera pas initialisé s'il s'agit d'un pointeur. Le parent doit être initialisé séparément.
  • Si le parent incorporé est un pointeur et n'est pas initialisé lors de l'initialisation de l'enfant, une erreur de déréférencement du pointeur nil se produira.
  • Les cas de définition de type et d'intégration permettent d'accéder aux champs du parent.
  • La définition de type n'autorise pas l'accès aux méthodes du parent, mais l'incorporation du parent le permet.

Vous pouvez le voir dans le code suivant.

exemple de travail sur le terrain de je

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")
}
92
TheHerk