如何在Go中实现抽象类?

Max*_*ysh 8 oop interface go

如何在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)
}
Run Code Online (Sandbox Code Playgroud)

这将无法编译,因为无法将接口用作接收器.

事实上,我已经回答了我的问题(见下面的答案).但是,它是一种实现这种逻辑的惯用方法吗?除语言简单之外,有没有任何理由不进行默认实现?

Adr*_*ian 14

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

去操场

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)
}
Run Code Online (Sandbox Code Playgroud)

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


icz*_*cza 7

如果要提供"默认"实现(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)
}
Run Code Online (Sandbox Code Playgroud)

具有功能值

在这个简单的例子中,这个更短.在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)
}
Run Code Online (Sandbox Code Playgroud)


Max*_*ysh 5

一个简单的解决方案是移动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)
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 这与其他语言的抽象类概念不太相似,但它可能是最惯用的 Go 解决方案(例如,这是许多核心 IO API 的工作方式)。 (3认同)
  • 这不仅仅是简单,它是界面的想法和目的.我建议你的答案更强. (2认同)