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.
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 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.
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 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 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 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 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
*/
}
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.⁵
¹__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