Home > Back-end >  What is the proper way using composition in Golang
What is the proper way using composition in Golang

Time:11-25

I'm OOP guy, recently I have to work on Golang the language that I haven't done it before. Though I already went through many articles talking about composition, I notice it's a bit tricky to use it properly on Golang

Let's say I have two examples of Golang composition, I don't know which one will be correct, and would it be different between of them? Thank you

First example

type BaseClass struct {
   db *DB
}

func NewBaseClass(db *DB) *BaseClass {
  return &BaseClass{db}
}

type ChildrenClass1 struct {
     baseClass *BaseClass
}

func NewChildrenClass1(db *DB) *ChildrenClass1 {
  baseClass := NewBaseClass(db)
  return &ChildrenClass1{baseClass}
}

type ChildrenClass2 struct {
     baseClass *BaseClass
}

func NewChildrenClass2(db *DB) *ChildrenClass2 {
  baseClass := NewBaseClass(db)
  return &ChildrenClass2{baseClass}
}

func main(){
  db := NewDB()
  chilrend1 := NewChildrenClass1(db)
  chilrend2 := NewChildrenClass2(db)
}

Second example

type BaseClass struct {
   db *DB
}

func NewBaseClass(db *DB) *BaseClass {
  return &BaseClass{db}
}

type ChildrenClass1 struct {
     baseClass *BaseClass
}

func NewChildrenClass1(baseClass *BaseClass) *ChildrenClass1 {
  return &ChildrenClass1{baseClass}
}

type ChildrenClass2 struct {
     baseClass *BaseClass
}

func NewChildrenClass2(baseClass *BaseClass) *ChildrenClass2 {
  return &ChildrenClass2{baseClass}
}

func main(){
  db := NewDB()
  baseClass := NewBaseClass(db)
  chilrend1 := NewChildrenClass1(baseClass)
  chilrend2 := NewChildrenClass2(baseClass)
}

CodePudding user response:

In Go you probably will not find a proper way to define composition or aggregation as you may find in many other languages that are OOP based. This is just because Go has no classes, no objects, no exceptions, and no templates.

But Go has structs. Structs are user-defined types. Struct types (with methods) serve similar purposes to classes in other languages.

Said that, let's see some common definitions and see what we can do:

Composition implies a relationship where the child cannot exist independent of the parent. Example: House (parent) and Room (child). Rooms don't exist separate to a House[1].

Aggregation, in other hand, implies a relationship where the child can exist independently of the parent. Example: Classroom (parent) and Student (child). Delete the Classroom and the Students still exist[1].

So, in aggregation and composition the "instance" "owns" an object of another type. But there is a subtle difference: Aggregation implies a relationship where the child can exist independently of the parent. Composition implies a relationship where the child cannot exist independent of the parent.

So far, that's what we know now from composition:

  • The child cannot exist without the parent
  • Composition refers to combining simpler types to make more complex ones
  • And of course, we mainly use it to be able to reuse code

Answer to your question: Both looks correct but,

  • The first example is closer to a composition, because the child will not exist without the parent;
  • The second example is more like an aggregation, because if you remove the parent, the child will keep existing.

I re-wrote your code on an attempt to exemplify it:

First example re-written

package main

//Lamp struct is here to suppress the *DB that was in the original example
type Lamp struct {}

type Room struct {
    Lamps *[]Lamp
}


func NewRoom(l *[]Lamp) *Room {
  return &Room{l}
}

type House1 struct {
    Room *Room
}

func NewHouse1(l *[]Lamp) *House1 {
  r := NewRoom(l)
  return &House1{r}
}

type House2 struct {
    Room *Room
}

func NewHouse2(l *[]Lamp) *House2 {
  r := NewRoom(l)
  return &House2{r}
}

func main(){
  lamps := []Lamp{}
  house1 := NewHouse1(&lamps)
  house2 := NewHouse2(&lamps)
}

Second example re-written:

package main

type LibraryCard struct {}

type Student struct {
   LibCard *LibraryCard
}

func NewStudent(l *LibraryCard) *Student {
  return &Student{l}
}

type Classroom1 struct {
    Student *Student
}

func NewClassroom1(s *Student) *Classroom1 {
  return &Classroom1{s}
}

type Classroom2 struct {
    Student *Student
}

func NewClassroom2(s *Student) *Classroom2 {
  return &Classroom2{s}
}

func main(){
  lc := new(LibraryCard)
  student := NewStudent(lc)
  classroom1 := NewClassroom1(student)
  classroom2 := NewClassroom2(student)
}
  • Related