paint-brush
Does Golang Follow the Object Oriented Programming Paradigm?by@yudaph
606 reads
606 reads

Does Golang Follow the Object Oriented Programming Paradigm?

by Yuda Prasetiya HaqqySeptember 26th, 2022
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Many are wondering whether Golang is a language that can apply the Object Oriented Programming paradigm. The answer is yes, in GO we can create an Object from a class/entity, a class in GO is also called a lightweight struct. The concept of Object-Oriented Programming has four main concepts, namely abstraction, encapsulation, inheritance, and polymorphism. In this article, we will try to answer the question of whether GO can implement the OOP paradigm. We will create two employee abstractions that will be used by the payroll department and manager.
featured image - Does Golang Follow the Object Oriented Programming Paradigm?
Yuda Prasetiya Haqqy HackerNoon profile picture

Many are wondering whether Golang is a language that can apply the Object Oriented Programming paradigm. Let's discuss this here.


First of all, we have to know what is meant by Object-Oriented Programming, after that we will try to answer the question of whether GO can implement Object-Oriented Programming.

Object-Oriented Programming (OOP)

By definition Object Oriented Programming means that this paradigm focuses on objects, objects in a language are made of classes or entities. So by definition, Object-Oriented Programming means an object-focused paradigm.¹


Conceptually, the Object-Oriented Programming paradigm has four main concepts, namely abstraction, encapsulation, inheritance, and polymorphism.²

Can GO implement Object-Oriented Programming?

Can GO create objects from classes or entities? Let's try to prove it

// class or entity or struct
type Person struct {
	// attributes
	FirstName string
	LastName  string
}

// method
func (p *Person) fullname() string {
	return p.FirstName + " " + p.LastName
}

func main() {
	john := Person{
		FirstName: "John",
		LastName:  "Travolta",
	}

	fmt.Println(john.fullname()) // John Travolta

}

The answer is yes, in GO we can create an Object from a class/entity, a class in GO is also called a lightweight struct.³ By definition, GO can apply the Object-Oriented Programming paradigm.


What about the main concept of Object-Oriented Programming?

This question has caused a lot of debate on "which GO language can implement Object-Oriented Programming or not". Let's discuss the concepts one by one.

Abstraction

Abstraction is a concept that allows us to hide complexity in the implementation, and only provide information that the user needs or uses.


For example, we will create two employee abstractions that will be used by the payroll department and employee managers.


The payroll section requires the name and amount of the salary, so the employee abstraction in the payroll package contains only the GetName function CalculateSalary which returns how much salary to use. we hide the difficulty of calculating salaries, calculating bonuses, and how performance affects bonuses is not required by payroll.

package payroll

// employee abstraction on finance
type EmployeeOnPayroll interface {
	GetName() string
	CalculateSalary() float64
}

func Work(employees ...EmployeeOnPayroll) {
	for _, employee := range employees {
		fmt.Println(employee.GetName(), "salary is", employee.CalculateSalary())
	}
}


Likewise, the manager only needs a function to assess performance, so the employee abstraction on the manager's side only contains the SetPerformance function, no other functions such as CalculateSalary are needed.

package manager

// employee abstraction on manager 
type EmployeeOnManager interface {
	SetPerformance(performance employee.Performance)
}

func Work(employees ...EmployeeOnManager) {
	for i, employee := range employees {
		employee.SetPerformance(employee.Performance(i % 3))
	}
}


In our employee package, the implementation, and all the hassle of calculating salaries and bonuses are hidden from other packages.

package employee

// performance constant
type Performance int32
const (
	Bad Performance = iota
	Good
	Great
)

type Employee struct {
	// attributes
	Name        string
	BasicSalary float64
	performance Performance
}

// employee method
func (e *Employee) GetName() string {
	return e.Name
}

func (e *Employee) SetPerformance(performance Performance) {
	e.performance = performance
}

func (e *Employee) CalculateSalary() float64 {
	salary := e.BasicSalary
	salary += e.calculateBonus()
	/*
		Do more calculation
	*/
	return salary
}

func (e *Employee) calculateBonus() float64 {
	switch e.performance {
	case Great:
		return e.BasicSalary
	case Good:
		return e.BasicSalary / 2
	default:
		return 0
	}
}


In the main package, we can see how the abstraction we have created works.

package main

/*
imports ...
*/

func main() {
	john := &employee.Employee{Name: "John", BasicSalary: 3000}
	jane := &employee.Employee{Name: "Jane", BasicSalary: 2000}
	bane := &employee.Employee{Name: "Bane", BasicSalary: 1500}
	manager.Work(john, jane, bane)
	payroll.Work(john, jane, bane)
	/*
	  John salary is 3000
	  Jane salary is 3000
	  Bane salary is 3000
	*/
}


Even though John, Jane, and Bane have different base salaries but because of the performance bonus, in the end, they get the same salary.

Encapsulation

Encapsulation is the concept of how we can protect data or functions on an entity, thus avoiding the wrong use or unwanted data changes.


Applying Encapsulation to Golang is quite easy, the use of lowercase in the first letter of naming attributes and methods indicates that the methods and attributes are private (cannot be accessed from outside the package). For example, we can see the performance attribute (line 15) and the calculateBonus method (line 36) in the employee package.

package employee

// performance constant
type Performance int32
const (
	Bad Performance = iota
	Good
	Great
)

type Employee struct {
	// attributes
	Name        string
	BasicSalary float64
	performance Performance // performance is private attribute
}

// employee method
func (e *Employee) GetName() string {
	return e.Name
}

func (e *Employee) SetPerformance(performance Performance) {
	e.performance = performance
}

func (e *Employee) CalculateSalary() float64 {
	salary := e.BasicSalary
	salary += e.calculateBonus()
	/*
		Do more calculation
	*/
	return salary
}

func (e *Employee) calculateBonus() float64 { // caculateBonus is private method
	switch e.performance {
	case Great:
		return e.BasicSalary
	case Good:
		return e.BasicSalary / 2
	default:
		return 0
	}
}

Inheritance

Inheritance is a concept where we can inherit methods and attributes from an existing class (parent class) to a newly created class (child class).


GO does not have Inheritance, but can use Composition as a solution even though it is still different from Inheritance, if Inheritance we can treat child classes as parent classes, while with Composition in GO we have to create interfaces if we want to use parent classes and child classes in the same place.

if you wonder why? you can read an article from Rob Pike here or Golang FAQ about inheritance here.


Composition in GO is quite easy to do, just add the base struct (parent class) to the newly created struct attribute. For example in line 4, we combine the Employee struct with the Supervisor struct. The Supervisor automatically gets all the methods and attributes of the Employee.

package employee

type Supervisor struct {
	Employee  // composite Employee to Supervisor
	SupervisorBonus float64
}

// Overriding CalculateSalary function from Employee Struct
func (s *Supervisor) CalculateSalary() float64 {
	salary := s.BasicSalary
	salary += s.calculateBonus()
  
        // add supervisor bonus to salary
	salary += s.SupervisorBonus
	/*
		Do more calculation
	*/
	return salary
}


Because it gets all the methods and attributes of the Employee, the Supervisor can be directly used in the EmployeeOnManager and EmployeeOnPayroll interfaces.

package main

/*
imports ...
*/

func main() {
	john := &employee.Employee{Name: "John", BasicSalary: 3000}
	jane := &employee.Supervisor{
		Employee:        employee.Employee{Name: "Jane", BasicSalary: 2000},
		SupervisorBonus: 500,
	}
	bane := &employee.Employee{Name: "Bane", BasicSalary: 1500}
	manager.Work(john, jane, bane)
	payroll.Work(john, jane, bane)
        /*
          John salary is 3000
          Jane salary is 3500
          Bane salary is 3000
        */
}

Polymorphism

Polymorphism consists of two words, namely Poly which means many, and Morph which means form. Usually, in Object-Oriented Programming, there are two ways to apply Polymorphism, namely Overloading and Overriding.


Overloading allows a class to have methods with the same name but have different parameters or return parameters.


Overriding allows the child class to have the same method (name, parameter, and return parameters) as the parent class but with a different implementation, when the method is called, the implementation of the child class will be used.


Golang can apply Overriding, but cannot apply Overloading. Why? You can read the explanation in GO FAQ here.


The implementation of overriding in GO is similar to Java or Javascript. After we do the composition for the child class we can create a method in the child class with the same name, parameters, and return parameters as the method we want to override.


For example, the CalculateSalary method on the Supervisor struct on line 9. The Supervisor actually already has a CalculateSalary method obtained from the Employee struct, but we need to override it because there are different ways of calculating.

package employee

type Supervisor struct {
	Employee  // composite Employee to Supervisor
	SupervisorBonus float64
}

// Overriding CalculateSalary function from Employee Struct
func (s *Supervisor) CalculateSalary() float64 {
	salary := s.BasicSalary
	salary += s.calculateBonus()
  
    // add supervisor bonus to salary
	salary += s.SupervisorBonus
	/*
		Do more calculation
	*/
	return salary
}


With overriding, Jane's salary calculation result changed from 3000 to 3500, because SupervisorBonus was added to her salary.

package main

/*
imports ...
*/

func main() {
	john := &employee.Employee{Name: "John", BasicSalary: 3000}
	jane := &employee.Supervisor{
		Employee:        employee.Employee{Name: "Jane", BasicSalary: 2000},
		SupervisorBonus: 500,
	}
	bane := &employee.Employee{Name: "Bane", BasicSalary: 1500}
	manager.Work(john, jane, bane)
	payroll.Work(john, jane, bane)
        /*
          John salary is 3000
          Jane salary is 3500
          Bane salary is 3000
        */
}

Conclusion

So is Golang an object-oriented programming language? I will quote two versions of the answer.

Yes, GO is Object-Oriented, but in an unusual way.⁴

Yes and No, although Go has types and methods, and allows "style" Object-Oriented Programming, but GO does not have a hierarchy (inheritance). The GO “interface” concept (because it uses Composition, not Inheritance) provides a different approach that GO's creators believe is easier to use in more general terms.⁵

References

¹__https://www.javatpoint.com/what-is-object-oriented-programming__
²__https://www.freecodecamp.org/news/object-oriented-programming-concepts-21bb035f7260/__
³__https://www.geeksforgeeks.org/structure-equality-in-golang/__
⁴__https://talks.golang.org/2012/chat.slide#5__
⁵__https://go.dev/doc/faq#Is_Go_an_object-oriented_language__




Thank you for reading, hopefully, it will be useful for all of us, and we can learn together to be better.

If you have questions or want to share or discuss, you can go through the comments column or through my contact on my profile 😁


Also Published here