Charlie
Charlie

Reputation: 2261

Why would you want to use composition in golang?

In the following code I show what I think is the difference between embedding (where methods get promoted) and composition (where methods are not promoted) in golang.

Why would you ever want to use composition in golang?

type obj1Inherited struct {
    obj2
}

type obj1Composed struct {
    someobj obj2
}

type obj2 struct {
}

func (o obj2) printTest() {
    fmt.Println("obj2")
}

func main() {
    o := obj1Inherited{}
    o.printTest() //fine - printTest is promoted

    obj1Composed := obj1Composed{}
    obj1Composed.someobj.printTest() //fine because I'm using the composed obj
    obj1Composed.printTest() //not fine - printTest is NOT promoted

Upvotes: 17

Views: 13306

Answers (4)

matt.s
matt.s

Reputation: 1746

It is worth going over the section on Embedding in Effective Go.

A common example is having a struct/map with a Mutex.

type SafeStruct struct {
    SomeField string 
    *sync.Mutex
}

It is much easier to type

safe := SafeStruct{SomeField: "init value"}

safe.Lock()
defer safe.Unlock()
safe.SomeField = "new value"

than having to either write appropriate wrapper functions (which are repetitive) or have the stutter of

safe.mutex.Unlock()

when the only thing you would ever do with the mutex field is access the methods (Lock() and Unlock() in this case)

This becomes even more helpful when you are trying to use multiple functions on the embedded field (that implemement an interface like io.ReadWriter).

Upvotes: 18

Chris Bao
Chris Bao

Reputation: 2888

In Golang, there are 3 types of embed

  • struct embed in struct
  • interface embed in interface
  • interface embed in struct

In detail you can refer to this post: https://eli.thegreenplace.net/2020/embedding-in-go-part-1-structs-in-structs/

Upvotes: 1

Dheerendra
Dheerendra

Reputation: 1568

I'll try to answer original question - sometimes people use "composition" instead of embedding to hide functionality of embedded struct. Not a great use case - but people prefer it sometimes.

type Obj1composed struct {
  notExportedObj1 obj1
}

func NewObj1Composed(someParam Params) Obj1composed {
...
}

func (o Obj1Composed) Print() {
  // Some heavy calculations here. Make Dozens of API calls
  // print some data
}

Upvotes: 0

Sumer
Sumer

Reputation: 2867

One more point I want to cover for first case in your example. If obj1Inherited and obj2 have a same name method then that method call (from obj1Inherited instance) will always execute obj1Inherited method.

To call obj2 method you can use the other approach of not promoting

Upvotes: 0

Related Questions