Search code examples
inheritancegomodeling

Best practices to deal with the lack of inheritance in Go


I am about to start writing a new application and I was thinking about programming it in Go, which I have no experience with. As Go doesn't support inheritance how would I faithfully translate a domain like this into Go, while still being in accordance with the philosophy of Go?:

  • A person has 2 legs and 2 arms and can walk.
  • A musician is a person, has an instrument and can play it, has partitions and can read them. But nobody can be only a musician, as a musicien has and plays a specific instrument.
  • A violonist is a musician that has a violon and can play the violon
  • A pianist is a musician that has a piano and can play the piano.
  • When a violonist/pianist is walking in the streets, everybody only sees him as a person.

Maybe we could reformulate the domain like this to remove any 'is a' relationships so that it would be translatable into Go:

  • A violinist has a person part, a musician part, a violin and can play the violin
  • The musician part of a violinist/pianist has partitions and can read them
  • When a violinist/pianist is walking in the streets, everybody can only see his person part
  • Etc

Would it be the best practice to model such a domain like this before translating into Go? Is it the way of thinking that the authors of Go want their users to have, when they decided to exclude inheritance in Go?


Solution

  • Go has just another point of view. You can think of objects and interfaces they match. It's closer to you second sentences:

    A violinist has a person part, a musician part, a violin and can play the violin

    This is interface Musician - it describes any person which have method Play()

    type Musician interface {
        Play()
    }
    

    For exapmple structure Violinist can have such a method:

    type Violinist struct {}
    func (v Violinist) Play() {}
    

    Also Pianist can play:

    type Pianist struct {}
    func (v Pianist) Play() {}
    

    They both match interface Musician. So you can have a musician variable and assign different musicians to it:

    var musician Musician
    musician = Violinist{}
    musician.Play()
    musician = Pianist{}
    musician.Play()
    

    Look how the same variable changes it's bahaviour. It's the same like Polyformism and not in OOP-way, but Go-way.

    When a violinist/pianist is walking in the streets, everybody can only see his person part

    The same way we may define a Walker interface - person who can walk a street and have appropriate method Walk for it:

    type Walker interface {
        Walk()
    }
    

    It's a sort of duck-typing: in our life if a person can play, it can be taken to the ensemble. In Go terms if a person has method Play() it can be assigned to a musician type variable.

    Etc

    Next you can make a combined interface embedding these both:

    type WalkingMusician interface {
        Musician
        Walker
    }
    

    This describes a person which can play and walk at a moment.

    var walkingMusician WalkingMusician
    walkingMusician = walkingViolinist
    walkingMusician.Walk()
    walkingMusician.Play()
    

    https://play.golang.org/p/ai-h4VDUpj