Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement an abstract class in Go?

Tags:

oop

interface

go

How to implement an abstract class in Go? As Go doesn't allow us to have fields in interfaces, that would be a stateless object. So, in other words, is it possible to have some kind of default implementation for a method in Go?

Consider an example:

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

This won't compile as it's not possible to use interface as a receiver.

In fact, I have already answered my question (see the answer below). However, is it an idiomatic way to implement such logic? Are there any reasons not to have a default implementation besides language's simplicity?

like image 330
Max Malysh Avatar asked May 15 '15 13:05

Max Malysh


People also ask

How do you implement abstract class in Golang?

It should not be possible to create direct instance of abstract class 2. It should provide default fields and methods. One important point to mention here is that all default method should have iAlpha as first argument, and if default method needs to call any unimplemented method they they will call on this interface.

How abstraction is achieved in Golang?

Golang is quite capable of implementing higher level abstractions, but the language designers choose not to implement certain abstractions into the programming language itself. You can use interfaces to create common abstraction that can be used by multiple types.

What is abstract class and how it is implemented?

Abstract classes are similar to interfaces. You cannot instantiate them, and they may contain a mix of methods declared with or without an implementation. However, with abstract classes, you can declare fields that are not static and final, and define public, protected, and private concrete methods.

Can abstract class have implementation methods?

Abstract class in Java is similar to interface except that it can contain default method implementation. An abstract class can have an abstract method without body and it can have methods with implementation also. abstract keyword is used to create a abstract class and method.


3 Answers

The other answers provide an alternative to your problem, however they proposed solution without using abstract classes/struct, and I guess if you were interested in using abstract class like solution, here is very precise solution to your problem:

Go plaground

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

If this is still not obvious how to use abstract classes/multi-inheritance in go-lang here is the post with comprehensive details. Abstract Classes In Go

like image 129
Adrian Avatar answered Oct 16 '22 20:10

Adrian


If you want to provide a "default" implementation (for Daemon.start()), that is not the characteristic of an interface (at least not in Go). That is a characteristic of a concrete (non-interface) type.

So Daemon in your case should be a concrete type, conveniently a struct since you want it to have fields. And the task to be done can be either a value of an interface type, or in a simple case just a function value (a simple case means it would only have one method).

With interface type

Try the complete app on the 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)
}

With a function value

In this simple case this one is shorter. Try it on the 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)
}
like image 14
icza Avatar answered Oct 16 '22 19:10

icza


An easy solution is to move daemon *Daemon to the argument list (thus removing start(...) from the interface):

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)
    ...
}
like image 7
Max Malysh Avatar answered Oct 16 '22 18:10

Max Malysh