为了账号安全,请及时绑定邮箱和手机立即绑定

如何在 Go 中实现抽象类?

如何在 Go 中实现抽象类?

Go
慕妹3242003 2021-10-04 18:12:03
如何在 Go 中实现抽象类?由于 Go 不允许我们在接口中有字段,这将是一个无状态对象。那么,换句话说,是否有可能为 Go 中的方法提供某种默认实现?考虑一个例子:type Daemon interface {    start(time.Duration)    doWork()}func (daemon *Daemon) start(duration time.Duration) {    ticker := time.NewTicker(duration)    // this will call daemon.doWork() periodically      go func() {        for {            <- ticker.C            daemon.doWork()        }    }()}type ConcreteDaemonA struct { foo int }type ConcreteDaemonB struct { bar int }func (daemon *ConcreteDaemonA) doWork() {    daemon.foo++    fmt.Println("A: ", daemon.foo)}func (daemon *ConcreteDaemonB) doWork() {    daemon.bar--    fmt.Println("B: ", daemon.bar)}func main() {    dA := new(ConcreteDaemonA)    dB := new(ConcreteDaemonB)    start(dA, 1 * time.Second)    start(dB, 5 * time.Second)    time.Sleep(100 * time.Second)}这不会编译,因为不可能将接口用作接收器。事实上,我已经回答了我的问题(见下面的答案)。但是,这是实现这种逻辑的惯用方法吗?除了语言的简单性之外,还有什么理由不使用默认实现吗?
查看完整描述

3 回答

?
烙印99

TA贡献1829条经验 获得超13个赞

其他答案为您的问题提供了替代方案,但是他们提出了不使用抽象类/结构的解决方案,我想如果您有兴趣使用类似抽象类的解决方案,这里是您问题的非常精确的解决方案:

package main


import (

    "fmt"

    "time"

)


type Daemon interface {

    start(time.Duration)

    doWork()

}


type AbstractDaemon struct {

    Daemon

}


func (a *AbstractDaemon) start(duration time.Duration) {

    ticker := time.NewTicker(duration)


    // this will call daemon.doWork() periodically  

    go func() {

        for {

            <- ticker.C

            a.doWork()

        }

    }()

}




type ConcreteDaemonA struct { 

*AbstractDaemon

foo int

}


func newConcreteDaemonA() *ConcreteDaemonA {

  a:=&AbstractDaemon{}

  r:=&ConcreteDaemonA{a, 0}

  a.Daemon = r

  return r

}



type ConcreteDaemonB struct { 

*AbstractDaemon

bar int

}


func newConcreteDaemonB() *ConcreteDaemonB {

  a:=&AbstractDaemon{}

  r:=&ConcreteDaemonB{a, 0}

  a.Daemon = r

  return r

}




func (a *ConcreteDaemonA) doWork() {

    a.foo++

    fmt.Println("A: ", a.foo)

}


func (b *ConcreteDaemonB) doWork() {

    b.bar--

    fmt.Println("B: ", b.bar)

}



func main() {

    var dA  Daemon = newConcreteDaemonA()

    var dB  Daemon = newConcreteDaemonB()


    dA.start(1 * time.Second)

    dB.start(5 * time.Second)


    time.Sleep(100 * time.Second)

}

如果这仍然不明显如何在 go-lang 中使用抽象类/多继承,这里是具有全面详细信息的帖子。


查看完整回答
反对 回复 2021-10-04
?
神不在的星期二

TA贡献1963条经验 获得超6个赞

如果您想提供“默认”实现(for Daemon.start()),那不是接口的特性(至少在 Go 中不是)。这是具体(非接口)类型的特征。


所以Daemon在你的情况下应该是一个具体的类型,struct因为你希望它有字段,所以很方便。并且要完成的任务可以是一个接口类型的值,或者在简单的情况下只是一个函数值(简单的情况意味着它只有一个方法)。


带接口类型

在Go Playground上试用完整的应用程序。


type Task interface {

    doWork()

}


type Daemon struct {

    task Task

}


func (d *Daemon) start(t time.Duration) {

    ticker := time.NewTicker(t)

    // this will call task.doWork() periodically

    go func() {

        for {

            <-ticker.C

            d.task.doWork()

        }

    }()

}


type MyTask struct{}


func (m MyTask) doWork() {

    fmt.Println("Doing my work")

}


func main() {

    d := Daemon{task: MyTask{}}

    d.start(time.Millisecond*300)


    time.Sleep(time.Second * 2)

}

带函数值

在这个简单的例子中,这个较短。在Go Playground上试一试。


type Daemon struct {

    task func()

}


func (d *Daemon) start(t time.Duration) {

    ticker := time.NewTicker(t)

    // this will call task() periodically

    go func() {

        for {

            <-ticker.C

            d.task()

        }

    }()

}


func main() {

    d := Daemon{task: func() {

        fmt.Println("Doing my work")

    }}

    d.start(time.Millisecond * 300)


    time.Sleep(time.Second * 2)

}


查看完整回答
反对 回复 2021-10-04
?
九州编程

TA贡献1785条经验 获得超4个赞

一个简单的解决方案是移动daemon *Daemon到参数列表(从而start(...)从界面中删除):


type Daemon interface {

    // start(time.Duration)

    doWork()

}


func start(daemon Daemon, duration time.Duration) { ... }


func main() {

    ...

    start(dA, 1 * time.Second)

    start(dB, 5 * time.Second)

    ...

}


查看完整回答
反对 回复 2021-10-04
  • 3 回答
  • 0 关注
  • 283 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信