วิธีการเพิ่มวิธีการใหม่ในประเภทที่มีอยู่ใน Go?


129

ฉันต้องการเพิ่มวิธีการใช้งานที่สะดวกใน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.Router ประเภทที่ไม่ใช่โลคัล

แล้วฉันจะบรรลุเป้าหมายนี้ได้อย่างไร? ฉันสร้างประเภทโครงสร้างใหม่ที่มีช่อง mux.Route และ mux.Router แบบไม่ระบุชื่อหรือไม่ หรืออย่างอื่น?


วิธีการขยายที่น่าสนใจถือเป็นไม่ใช่เชิงวัตถุ ( “extension methods are not object-oriented”) สำหรับ C # แต่เมื่อดูในวันนี้ฉันจำอินเทอร์เฟซของ Go ได้ทันที (และวิธีการคิดใหม่ในการวางแนววัตถุ) จากนั้นฉันก็มีคำถามนี้
Wolf

คำตอบ:


174

ตามที่คอมไพเลอร์กล่าวถึงคุณไม่สามารถขยายประเภทที่มีอยู่ในแพ็คเกจอื่นได้ คุณสามารถกำหนดนามแฝงหรือแพ็คเกจย่อยของคุณเองได้ดังนี้:

type MyRouter mux.Router

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

หรือโดยการฝังเราเตอร์เดิม:

type MyRouter struct {
    *mux.Router
}

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

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

10
หรือใช้แค่ฟังก์ชั่น ... ?
พอฮันคิน

5
@Paul การทำเช่นนี้จำเป็นต้องใช้เพื่อแทนที่ฟังก์ชันเช่น String () และ MarshalJSON ()
Riking

31
ถ้าคุณทำส่วนแรกแล้วคุณจะบังคับmux.Routerอินสแตนซ์กับMyRouters ได้อย่างไร? เช่นถ้าคุณมีห้องสมุดที่ส่งคืนmux.Routerแต่คุณต้องการใช้วิธีการใหม่ของคุณ?
docwhat

วิธีใช้วิธีแก้ปัญหาแรก MyRouter (เราเตอร์)
tfzxyinhao

การฝังดูเหมือนจะใช้งานได้จริงกว่าเล็กน้อย
ivanjovanovic

124

ฉันต้องการที่จะขยายตัวในคำตอบที่ได้รับจาก @jimt ที่นี่ คำตอบนั้นถูกต้องและช่วยฉันอย่างมากในการแยกแยะสิ่งนี้ อย่างไรก็ตามมีข้อแม้บางประการสำหรับทั้งสองวิธี (นามแฝงการฝัง) ที่ฉันประสบปัญหา

หมายเหตุ : ฉันใช้คำว่า parent และ child แม้ว่าฉันจะไม่แน่ใจว่าเหมาะที่สุดสำหรับการเรียบเรียง โดยทั่วไปพาเรนต์คือประเภทที่คุณต้องการแก้ไขในเครื่อง Child เป็นประเภทใหม่ที่พยายามใช้การปรับเปลี่ยนดังกล่าว

วิธีที่ 1 - คำจำกัดความประเภท

type child parent
// or
type MyThing imported.Thing
  • ให้การเข้าถึงฟิลด์
  • ไม่ให้การเข้าถึงวิธีการ

วิธีที่ 2 - การฝัง ( เอกสารอย่างเป็นทางการ )

type child struct {
    parent
}
// or with import and pointer
type MyThing struct {
    *imported.Thing
}
  • ให้การเข้าถึงฟิลด์
  • ให้การเข้าถึงวิธีการ
  • ต้องมีการพิจารณาเพื่อเริ่มต้น

สรุป

  • การใช้วิธีการจัดองค์ประกอบพาเรนต์แบบฝังจะไม่เริ่มต้นหากเป็นตัวชี้ ต้องเริ่มต้นพาเรนต์แยกกัน
  • หากพาเรนต์ฝังตัวเป็นตัวชี้และไม่ได้เตรียมใช้งานเมื่อลูกถูกเตรียมใช้งานจะเกิดข้อผิดพลาดในการกำหนดค่าตัวชี้ nil
  • ทั้งนิยามชนิดและกรณีฝังให้การเข้าถึงฟิลด์ของพาเรนต์
  • นิยามประเภทไม่อนุญาตให้เข้าถึงเมธอดของพาเรนต์ แต่การฝังพาเรนต์ทำ

คุณสามารถดูสิ่งนี้ได้ในรหัสต่อไปนี้

ตัวอย่างการทำงานในสนามเด็กเล่น

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

โพสต์ของคุณมีประโยชน์มากเนื่องจากแสดงการวิจัยและความพยายามอย่างมากในการพยายามเปรียบเทียบทีละจุดในแต่ละจุด .. ขออนุญาตให้ฉันแนะนำให้คุณคิดว่าเกิดอะไรขึ้นในแง่ของการแปลงเป็นอินเทอร์เฟซที่กำหนด ฉันหมายความว่าถ้าคุณมีโครงสร้างและคุณต้องการโครงสร้างนั้น (จากผู้ขายบุคคลที่สามสมมติ) คุณต้องการปรับให้เข้ากับอินเทอร์เฟซที่กำหนดคุณจะจัดการให้ใครได้รับสิ่งนั้น? คุณสามารถใช้นามแฝงประเภทหรือประเภทการฝังสำหรับสิ่งนั้น
วิกเตอร์

@ วิกเตอร์ฉันไม่ทำตามคำถามของคุณ แต่ฉันคิดว่าคุณถามวิธีรับโครงสร้างที่คุณไม่ได้ควบคุมเพื่อตอบสนองอินเทอร์เฟซที่กำหนด คำตอบสั้น ๆ คุณไม่ได้ยกเว้นโดยการมีส่วนร่วมใน codebase นั้น อย่างไรก็ตามการใช้เนื้อหาในโพสต์นี้คุณสามารถสร้างโครงสร้างใหม่จากอันแรกจากนั้นใช้อินเทอร์เฟซบนโครงสร้างนั้น ดูตัวอย่างสนามเด็กเล่นนี้
TheHerk

สวัสดี @TheHerk สิ่งที่ฉันตั้งเป้าไว้คือให้คุณชี้ให้เห็นความแตกต่างเมื่อ "ขยาย" โครงสร้างจากแพ็คเกจอื่น สำหรับฉันดูเหมือนว่ามีสองวิธีในการสร้างสิ่งนี้โดยใช้นามแฝงประเภท (ตัวอย่างของคุณ) และการใช้ type embed ( play.golang.org/p/psejeXYbz5T ) สำหรับฉันดูเหมือนว่านามแฝงประเภทนั้นจะทำให้การแปลงง่ายขึ้นเนื่องจากคุณต้องการการแปลงประเภทเท่านั้นหากคุณใช้ type wrap คุณจำเป็นต้องอ้างอิงโครงสร้าง "พาเรนต์" โดยใช้จุดดังนั้นการเข้าถึงประเภทหลักนั้นเอง ฉันเดาว่าขึ้นอยู่กับรหัสลูกค้า ...
วิกเตอร์

โปรดดูแรงจูงใจของหัวข้อนี้ที่นี่stackoverflow.com/a/28800807/903998ติดตามความคิดเห็นและฉันหวังว่าคุณจะเห็นประเด็นของฉัน
วิกเตอร์

ฉันหวังว่าฉันจะทำตามความหมายของคุณ แต่ฉันยังคงมีปัญหา ในคำตอบที่เรากำลังเขียนความคิดเห็นเหล่านี้ฉันจะอธิบายทั้งการฝังและการใช้นามแฝงรวมถึงข้อดีและข้อเสียของแต่ละข้อ ฉันไม่ได้สนับสนุนคนอื่น อาจเป็นไปได้ว่าคุณกำลังแนะนำว่าฉันพลาดหนึ่งในข้อดีหรือข้อเสียเหล่านั้น
TheHerk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.