OOP in Go
Object oriented programming (OOP) is a common pattern in many programming languages. Object oriented programming has many advantages like reusability, polymorphism and encapsulation. The concept of OOP can be implemented using Go but with different approach. Basically, some concepts in OOP like class, interface and methods are supported in Go but there is no type hierarchy. In Golang official documentation in FAQ section there is an interesting answer from a question "Is Go an object-oriented language?" that you can check here
Pointer
Pointer is an addressing mechanism that supported in Go. In pointer, there are two operators called addressing or pointing into certain variable (&
) and dereference (*
).
Let's take a look at this example:
func main() {
a := 42
//prints out the value of a
fmt.Println("value of a: ", a)
//prints out the memory address that pointing to "a" variable
fmt.Println("memory address of a: ", &a)
}
Output:
value of a: 42
memory address of a: 0xc000014080
Based on that code, &
operator is used to addressing into certain variable.
With pointer, a variable is capable to addressing into another variable. For example, a b
variable can addressing into another variable (in this code the variable is called a
). Here it is the code example based on that case.
func main() {
a := 42
//prints out the value of a
fmt.Println("value of a: ", a)
//prints out the memory address that pointing to "a" variable
fmt.Println("memory address of a: ", &a)
b := &a
//prints out the memory address of "b" variable
fmt.Println("value of b:", b)
//to prints out the value of b, use * (dereference) operator
fmt.Println("value of b using * operator: ", *b)
}
Output:
value of a: 42
memory address of a: 0xc000014080
value of b: 0xc000014080
value of b using * operator: 42
Notice that the value of b and value of a are equals because the b
variable contains the memory address that same with a
variable.
The illustration looks like this.
If the value of a
changes, the value of b
also changes as well.
//changes the value of "a"
a = 45
fmt.Println("value of a: ", a)
fmt.Println("value of b: ", *b)
Output:
value of a: 45
value of b: 45
struct
Class is a concept that mainly used in OOP. In Go, there is a similiar concept in class that called struct
. To declare a struct
can be done by using this syntax:
type structName struct {
//field name type
member int
member2 string
member3 []string
}
Here it is an example of using struct
. This struct illustrates a person that has a behavior to say something.
//declare a struct called person
type person struct {
name string
age int
}
//declare a method say() with type person as receiver
func (p person) say() {
fmt.Println("Hello, my name is: ", p.name)
}
To use the defined struct, assign defined struct into variable like this:
//instantiate the person
p1 := person{name: "Enki Gilbert", age: 42}
//call a method say()
p1.say()
Here it is the complete code:
package main
import "fmt"
//declare a struct called person
type person struct {
name string
age int
}
//declare a method say() with type person as receiver
func (p person) say() {
fmt.Println("Hello, my name is: ", p.name)
}
func main() {
//instantiate the person
p1 := person{name: "Enki Gilbert", age: 42}
//call a method say()
p1.say()
}
Output:
Hello, my name is: Enki Gilbert
The alternative syntax of using defined struct can be done by ignoring the field name.
//instantiate the person
p1 := person{"Enki Gilbert", 42}
The anonymous struct is also available in Go.
s1 := struct{
//declare some fields
field1 int
field2 []string
}{
//instantiate directly
field1: 12,
field2: []string{"hi","mate"},
}
Composition
The inheritance feature isn't supported in Go, we can use composition mechanism instead. Here it is the example of struct
composition:
//declare a struct called person
type person struct {
name string
age int
}
//composition example
//declare a struct called manager that includes another struct called person
type manager struct {
person
team string
}
//declare a method say() with type person as receiver
func (p person) say() {
fmt.Println("Hello, my name is: ", p.name)
}
func main() {
//instantiate the person
p1 := person{"Enki Gilbert", 42}
//instantiate the manager and assign person field by using p1 variable
m1 := manager{person: p1, team: "Racing Team Solvalou"}
//the say() method is still available
m1.say()
}
Output:
Hello, my name is: Enki Gilbert
Based on that code, we declare a struct called manager
that compose another struct called person
. The say()
method is still available to manager
struct because it composes another struct (person
) that has a say()
method.
The alternative syntax of instatiate the struct that has a struct composition:
//instatiate the person struct into person field directly
m1 := manager{person: person{"Enki Gilbert", 42}, team: "Racing Team Solvalou"}
Interface
Interface is a powerful concept in programming. Interface is similiar with struct but it only contains some abstract methods. In Go, interface defines an generic abstraction of behaviors. To declare an interface, can be done by using this syntax:
//type name interface
type name interface {
//declare some methods
method1()
method2() int
}
Here it is the example of using interface.
//declare a rectangle struct
type rectangle struct {
length int
width int
}
//declare an interface with area() as a member
type shape interface {
area() int
}
//declare a method area()
//the rectangle struct implements area() method in shape interface
func (r rectangle) area() int {
return r.length * r.width
}
//declare a method with type shape as a parameter
func info(s shape) {
fmt.Println("the area: ", s.area())
}
func main() {
r1 := rectangle{12, 12}
//r1 is a rectangle type. rectangle implements all methods in shape interface.
info(r1)
}
Output:
the area: 144
Based on that example, we declare a struct called rectangle
and an interface called shape
. The rectangle
implements a method called area()
in shape
interface. There is a method called info()
with shape
type as a parameter. The info()
method's parameter is accessible for every struct that implements all methods in shape
interface.
If there is another struct, in this case called square
. The info()
method is also available because square
struct is also implements all methods in shape
interface.
type square struct {
side int
}
//the square struct implements area() method in shape interface
func (s square) area() int {
return s.side * s.side
}
Here it is the complete example:
package main
import (
"fmt"
)
//declare a rectangle struct
type rectangle struct {
length int
width int
}
//declare a square struct
type square struct {
side int
}
//declare an interface with area() as a member
type shape interface {
area() int
}
//declare a method area()
//the rectangle struct implements area() method in shape interface
func (r rectangle) area() int {
return r.length * r.width
}
//the square struct implements area() method in shape interface
func (s square) area() int {
return s.side * s.side
}
//declare a method with type shape as a parameter
/**
anything that implements all methods in shape interface is considered as a shape in general.
for this case the rectangle and square is a shape because implements all methods in shape interface
**/
func info(s shape) {
fmt.Println("the area: ", s.area())
}
func main() {
r1 := rectangle{12, 12}
info(r1)
s1 := square{25}
info(s1)
}
Output:
the area: 144
the area: 625
The composition of interface is also available. The example can be checked in io package here.
Method sets
Method set is a function that associates with certain type. For example the area()
is a function that associates with shape
type. If the receiver of method set is a value, then the method can be used by a value or a pointer of a value.
For example, this area()
method is available for value and pointer of type shape
(in this example is a rectangle).
//shape interface
type shape interface {
area() int
}
func (r rectangle) area() int {
return r.length * r.width
}
//used in info() function
func info(s shape) {
fmt.Println("the area: ", s.area())
}
func main() {
r1 := rectangle{12, 12}
//with value
info(r1)
//with pointer
info(&r1)
}
Output:
the area: 144
the area: 144
If the receiver of method set is a pointer, then the method is only available with pointer.
Example:
//the receiver is a pointer
func (r *rectangle) area() int {
return r.length * r.width
}
func info(s shape) {
fmt.Println("the area: ", s.area())
}
func main() {
r1 := rectangle{12, 12}
//with value, throw an error
// info(r1)
//with pointer
info(&r1)
}
Output:
the area: 144
Notes
- Learn more about struct in here
- Learn more about pointer in here
- Learn more about method sets in here
I hope this article helpful for helping to learn the Go programming language. If you have any thoughts or feedbacks, you can write it in the discussion section below.
Top comments (0)