Search code examples
oopgodynamic-dispatch

Idiomatic way to mimic proper dynamic dispatch in Go


I am coming to Go from Java and some things confuses me.

For example, let's consider the following code:

package main

import (
    "fmt"
)

type I interface {
    Do()
    MegaDo()
}

type A struct {
}

func (a *A) Do() {
    fmt.Println("A")
}

func (a *A) MegaDo() {
    a.Do()
}

type B struct {
    A
}

func (a *B) Do() {
    fmt.Println("B")
}

var i I

func main() {
    fmt.Println("Hello, playground")

    var i I = &B{}

    i.MegaDo()
}

Here we have an interface I with methods Do() and MegaDo() . Struct A implements both methods and MegaDo calls Do internally. And B is composed over A and overrides only Do()

If I'll mimic the same code in Java I would expect it to print "B". But in Go it prints "A".

While I, kind of, understand why it happens (because it's embedding and not inheritance) I wonder how I can mimic the same thing in Go. For example I have two implementations of the same interface that differs only a little. How can I maximize code reusage in this case? I can't believe that in order to customize a bit logic in one implementation I have to copy-paste everything and just fix a little part in my code. Maybe there is some idiomatic way to do this in Go?


Solution

  • Your question is too abstract to answer well, but I hope this helps.

    Re-think the design starting with the real problem (business need etc) you're trying to solve, and don't try to solve it in Go using a Java design. Go has no inheritance and interfaces are its only form of polymorphism; you cannot "mimic dynamic dispatch" in Go in any reasonable way.

    Specifically, in regards to this:

    I have two implementations of the same interface that differs only a little. How can I maximize code reusage in this case? I can't believe that in order to customize a bit logic in one implementation I have to copy-paste everything and just fix a little part in my code.

    Re-think your design in terms of code re-use rather than class hierarchy, because there is none. If you have two implementations of the same interface, that's fine! Go has interfaces and they work great. If you have a bunch of code repeated in both implementations, either a) abstract the shared code into functions that both implementations can call, or b) if the differences really are that small, maybe it should be a single implementation with some simple switching logic.