goinheritancecomposition

Understanding Inheritance vs Composition in golang


Coming from Java background, I am unable to understand how inheritance can be achieved using Composition or How composition solves some of the common solutions achieved by Inheritance?

interface ICommand {
    void Save(Records data)
    Records LoadRecords()
    Info GetInfo()
}

abstract class BaseCommand : ICommand {
    Records LoadRecords()  {
        var info = GetInfo()
        //implement common method.
    }
}

class CommandABC : BaseCommand {
    Info GetInfo(){
        return info;
    }

    void Save(Records data){
        // implement
    }
}

c = new CommandABC();
c.LoadRecords(); // BaseCommand.LoadRecords -> CommandABC.Info -> Return records
c.Save(); //Command ABC.Save

I want to achieve the same functionality in Go using composition. After all this is fair design and should be good to implement in Go.

type ICommand interface {
    void Save(data Records)
    LoadRecords() Records
    GetInfo() Info
}

type BaseCommand struct {
    ICommand  //no explicit inheritance. Using composition
}

func(c BaseCommand) LoadRecords() {
    info := c.GetInfo()
    //implement common method
}

type CommandABC struct {
    BaseCommand //Composition is bad choice here?
}

func(c CommandABC) Save(data Records) {
    //implement
}

func(c CommandABC) GetInfo() Info {
    //implement
}

func main(){
    c := CommandABC{}
    c.LoadRecords(); // BaseCommand.LoadRecords -> fails to call GetInfo since ICommand is nil
    c.Save(); //Command ABC.Save
}

It can be made to work out like this

func main(){
    c := CommandABC{}
    c.ICommand = c //so akward. don't even understand why I am doing this
    c.LoadRecords(); // BaseCommand.LoadRecords -> fails to call GetInfo since ICommand is nil
    c.Save(); //Command ABC.Save
}

Can anyone enlighten me on achieving such functionality from Go design perspective.

My concerns/queries more around understanding, how to use composition for such problems/code reusability with better design patterns going forward.


Solution

  • You could do it a few different ways, but the most idiomatic is probably something along these lines. It's hard to give a detailed answer based on a contrived example with no details and most code elided, but I think this gets to where you're trying to go.

    type Infoer interface {
       GetInfo() Info
    }
    
    func LoadRecords(i Infoer) Records  {
        var info = i.GetInfo()
        //implement common method.
    }
    
    type CommandABC struct {
        info Info
    }
    
    func (c CommandABC) GetInfo() Info {
        return c.info;
    }
    
    func (CommandABC) Save(data Records){
        // implement
    }
    
    c := CommandABC{};
    records := LoadRecords(c);
    c.Save(records);