visit
// 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.
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
}
}
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
*/
}
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
}
}
if you wonder why? you can read an article from Rob Pike here or Golang FAQ about inheritance .
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
*/
}
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
*/
}
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.⁵
¹____
²____
³____
⁴____
⁵____
Also Published