Go'da var olan bir türe yeni yöntemler nasıl eklenir?


129

gorilla/muxRota ve Yönlendirici türlerine bir kolaylık kullanım yöntemi eklemek istiyorum :

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

ama derleyici beni bilgilendiriyor

Yerel olmayan tür mux üzerinde yeni yöntemler tanımlanamıyor.

Peki bunu nasıl başarabilirim? Anonim mux.Route ve mux.Router alanları olan yeni bir struct türü oluşturmalı mıyım? Veya başka bir şey?


İlginç bir şekilde genişletme yöntemleri, “extension methods are not object-oriented”C # için nesne yönelimli olmayan ( ) olarak kabul edilir , ancak bugün onlara baktığımda, Go'nun arayüzlerini (ve nesne yönünü yeniden düşünme yaklaşımını) hemen hatırladım ve sonra bu soruyu sormuştum.
Wolf

Yanıtlar:


174

Derleyicinin bahsettiği gibi, başka bir pakette mevcut türleri genişletemezsiniz. Kendi takma adınızı veya alt paketinizi aşağıdaki gibi tanımlayabilirsiniz:

type MyRouter mux.Router

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

veya orijinal yönlendiriciyi yerleştirerek:

type MyRouter struct {
    *mux.Router
}

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

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

10
Veya sadece bir işlev kullanın ...?
Paul Hankin

5
@Paul, String () ve MarshalJSON () gibi işlevleri geçersiz kılmak için gereklidir
Riking

31
İlk bölümü yaparsanız, mux.Routerörnekleri MyRouters'ye nasıl zorlarsınız ? Örneğin, dönen bir kitaplığınız varsa mux.Routerancak yeni yöntemlerinizi kullanmak istiyorsanız?
docwhat

ilk çözüm nasıl kullanılır? MyRouter (yönlendirici)
tfzxyinhao

gömme kullanımı biraz daha pratik görünüyor.
ivanjovanovic

124

Burada @jimt tarafından verilen cevabı genişletmek istedim . Bu cevap doğrudur ve bunu çözmemde bana çok yardımcı oldu. Bununla birlikte, her iki yöntemde de (takma ad, gömme) sorun yaşadığım bazı uyarılar var.

not : Beste için en iyisinin bu olduğundan emin değilim, ancak ebeveyn ve çocuk terimlerini kullanıyorum. Temel olarak ana, yerel olarak değiştirmek istediğiniz türdür. Çocuk, bu değişikliği uygulamaya çalışan yeni türdür.

Yöntem 1 - Tür Tanımı

type child parent
// or
type MyThing imported.Thing
  • Alanlara erişim sağlar.
  • Yöntemlere erişim sağlamaz.

Yöntem 2 - Gömme ( resmi belgeler )

type child struct {
    parent
}
// or with import and pointer
type MyThing struct {
    *imported.Thing
}
  • Alanlara erişim sağlar.
  • Yöntemlere erişim sağlar.
  • Başlatma için dikkate alınması gerekir.

özet

  • Kompozisyon yöntemini kullanarak, gömülü üst öğe bir işaretçi ise başlatılmayacaktır. Ebeveyn, ayrı olarak başlatılmalıdır.
  • Gömülü üst öğe bir işaretçi ise ve çocuk başlatıldığında başlatılmamışsa, sıfır işaretçi referans hatası oluşacaktır.
  • Hem tür tanımı hem de yerleştirme durumları, üst öğenin alanlarına erişim sağlar.
  • Tür tanımı ebeveynin yöntemlerine erişime izin vermez, ancak ebeveynin gömülmesi izin verir.

Bunu aşağıdaki kodda görebilirsiniz.

oyun alanında çalışma örneği

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

Gönderiniz çok yararlıdır, çünkü her tekniği tek tek karşılaştırmaya çalışırken çok fazla araştırma ve çaba gösterir. Belirli bir arayüze dönüşüm açısından neler olduğunu düşünmenizi teşvik etmeme izin verin. Demek istediğim, eğer bir yapınız varsa ve bu yapıyı istiyorsanız (bir üçüncü taraf satıcıdan varsayalım) belirli bir arayüze uyum sağlamak istiyorsanız, bunu kim elde edersiniz? Bunun için tür takma adı veya tür yerleştirme kullanabilirsiniz.
Victor

@Victor Sorunuzu takip etmiyorum, ancak belirli bir arayüzü tatmin etmek için kontrol etmediğiniz bir yapıyı nasıl elde edeceğinizi sorduğunuzu düşünüyorum. Kısa cevap, bu kod tabanına katkıda bulunmaktan başka yapmazsınız. Ancak, bu gönderideki materyali kullanarak, ilkinden başka bir yapı oluşturabilir, ardından arayüzü o yapı üzerinde uygulayabilirsiniz. Bu oyun alanı örneğine bakın .
TheHerk

merhaba @TheHerk, Amacım, bir yapıyı başka bir paketten "genişletirken" başka bir farklılığa işaret etmektir. Bana öyle geliyor ki, bunu arşivlemenin iki yolu var: tür takma adı (sizin örneğiniz) ve embed türü ( play.golang.org/p/psejeXYbz5T ). Bana göre bu tür takma adı, yalnızca bir tür dönüştürmeye ihtiyaç duyduğunuz için dönüştürmeyi kolaylaştırıyor gibi görünüyor , tür sarma kullanıyorsanız, bir nokta kullanarak "ana" yapıya başvurmanız ve böylece ana türün kendisine erişmeniz gerekir. Sanırım müşteri koduna kalmış ...
Victor

lütfen, bu konunun motivasyonuna buradan bakın stackoverflow.com/a/28800807/903998 , yorumları takip edin ve umarım benim fikrimi görürsünüz
Victor

Keşke anlamını takip edebilseydim, ama hala sorun yaşıyorum. Bu yorumları yazdığımız yanıtta, her birinin avantajları ve dezavantajları dahil olmak üzere hem yerleştirmeyi hem de örtüştürmeyi açıklıyorum. Ben birini diğerine savunmuyorum. Bu artı veya eksilerden birini kaçırdığımı öneriyor olabilirsiniz.
TheHerk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.