Golang中如何覆写原结构体的函数?

Golang中如何覆写原结构体的函数? 我有三个包:routercontrollercustom

controllercustom 导入了一个默认结构体,如果在 controller 包中我创建了一个在 custom 中已存在的函数,那么来自 customServeHTTP 应该执行 controller 中的函数,而不是原始的那个。


package router

import "project/controller"
router.Handler("GET", "/service", &controller.Service{})
package controller

import "project/custom"

type Service struct{ custom.PageController }

func (c *Service) Sample() {
	fmt.Println("from controller")
}
package custom

type PageController struct {}

func (c *PageController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	c.Sample()
	w.Write([]byte("This is my page"))
}

func (c *PageController) Sample(){
	fmt.Println("from custom")
}

根据以上解释,我的目标是输出结果为: "from controller"

但目前的输出是: "from custom"


更多关于Golang中如何覆写原结构体的函数?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

在这个示例中,它对我来说是有效的。

抱歉,我的示例有点不同;我会检查得更仔细。

更多关于Golang中如何覆写原结构体的函数?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Willy:

我应该在什么时候使用函数和结构体来处理客户端请求。

我认为对此没有严格的规则。这在很大程度上取决于上下文,也许也有一点个人偏好的因素。

你好,

在这个示例中,我使用了接口来实现目标。

当我需要做类似你需求的事情时,我通常是这样做的。

说实话,我对此并没有特别强烈的看法。

通常,对于非简单的应用程序/服务,我会使用 go-kit 来处理业务逻辑,并使用 go-chi 作为 HTTP 路由器。在这种情况下,这两个工具包都会施加一些约束。

go-kit 的一个“好”处(不止一个)是,它强制要求使用接口进行依赖注入。

感谢你们的回答,让我明白了代码中需要改进的地方。显然,如果我想重写 Sample(),我必须先在 controller 包中重写 ServeHTTP(w http.ResponseWriter, r *http.Request),但这会偏离我的目标。当然,我也可以采纳你们的一些建议,但我了解到,我原本寻求的工作方式类似于其他语言中的继承,而 Go 不应该以这种方式使用,因此我将改变我的项目设计。

但现在我想知道你们对何时更适合使用以下两种选项的看法:

router.Handler("GET", "/service", &controller.Service{})
router.GET("/service", othercontroller.Service())

正如你们所见,区别在于第二个选项接收来自 othercontroller 包的一个函数,而第一个选项接收一个结构体,并且两者都符合以下签名:

ServeHTTP(w http.ResponseWriter, r *http.Request)

我的问题,如果之前没有表达清楚,可以归结为:我应该何时使用函数,何时使用结构体来处理客户端请求。

你好 @Willy

你的代码无法编译,因为方法 func (c *Controller) Sample() 没有对应的 Controller 类型,你是指 func (c *PageController) Sample() 吗?

为了简化,我将所有包合并成了一个 - Playground 链接

package main

import "fmt"

type PageController struct{}

func (c *PageController) Sample() {
	fmt.Println("from custom")
}

type Service struct {
	PageController
}

func (c *Service) Sample() {
	fmt.Println("from controller")
}

func main() {
	s := Service{}
	s.Sample()                // "from controller"
	s.PageController.Sample() // "from custom"

	p := PageController{}
	p.Sample() // "from custom"
}

s.Sample() 总是 调用 func (c *Service) Sample()。为了调用嵌入字段 PageControllerSample() 方法,你需要明确调用路径:s.PageController.Sample()

@Massimo.Costa 关于接口的例子是实现这类动态行为的一个好方法。

在Go语言中,结构体嵌入不会自动实现多态行为。当PageControllerServeHTTP调用c.Sample()时,它调用的是PageController自己的Sample方法,而不是嵌入结构体的重写版本。

要实现你期望的行为,需要修改ServeHTTP的实现方式。以下是解决方案:

package custom

import (
    "fmt"
    "net/http"
)

type PageController struct{}

func (c *PageController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 使用类型断言检查是否有重写的Sample方法
    if s, ok := interface{}(c).(interface{ Sample() }); ok {
        s.Sample()
    } else {
        c.Sample()
    }
    w.Write([]byte("This is my page"))
}

func (c *PageController) Sample() {
    fmt.Println("from custom")
}

更好的设计是定义一个接口,并使用接口方法调用:

package custom

import (
    "fmt"
    "net/http"
)

type Sampler interface {
    Sample()
}

type PageController struct{}

func (c *PageController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 将c转换为Sampler接口
    if sampler, ok := interface{}(c).(Sampler); ok {
        sampler.Sample()
    } else {
        c.Sample()
    }
    w.Write([]byte("This is my page"))
}

func (c *PageController) Sample() {
    fmt.Println("from custom")
}

这样,当controller.Service嵌入custom.PageController并重写Sample()方法时,ServeHTTP会调用正确的版本:

package controller

import (
    "fmt"
    "project/custom"
)

type Service struct {
    custom.PageController
}

func (c *Service) Sample() {
    fmt.Println("from controller")
}

// Service现在隐式实现了custom.Sampler接口

输出结果将是:

from controller

这个解决方案利用了Go的接口系统,通过类型断言在运行时检查并调用正确的方法实现。

回到顶部