Object-Oriented Programming (OOP) is a programming paradigm based on the concept of objects which can contain properties and methods. Properties are variables that contain data. Methods are functions to manipulate data.
There are four pillars of OOP; Abstraction, Encapsulation, Inheritance, and Polymorphism. A programming language that can be said to supports OOP must achieve these pillars. It looks like Go is a procedural programming language. But it supports the OOP paradigm, even if it looks a little tricky.
Abstraction
Abstract is about hiding implementation details. We can understand the objects that contain properties and methods without having to understand the details.
packagemainimport"fmt"typeanimalstruct{namestringfeetinthasPawsbool}typemonsterstruct{animalabilities[]string}func(animalanimal)print(){fmt.Println(animal.name,"has",animal.feet,"feet.")ifanimal.hasPaws{fmt.Println(animal.name,"has paws.")}}func(monstermonster)print(){fmt.Println(monster.name,"is a monster!!!")fmt.Println(monster.name,"has:")for_,ability:=rangemonster.abilities{fmt.Println("-",ability)}}funcmain(){dragon:=monster{animal{"Dragon",4,true},[]string{"Nuclear blast","Ice smoke"},}dragon.print()}
There are two print methods. One from the animal object and one from the monster. They have different results. Here is the output from the code above.
Dragon is a monster!!!
Dragon has:
- Nuclear blast
- Ice smoke
Encapsulation
Lastly, Encapsulation. I purposely explained it last. Encapsulation is the action of enclosing something in or as if in a capsule. It is about making private or public for some properties or methods.
packagemainimport"fmt"typepropertiesinterface{setName(namestring)getName()stringsetFeet(feetint)getFeet()intsetHasPaws(hasPawsbool)getHasPaws()bool}typeanimalstruct{namestringfeetinthasPawsbool}typemonsterstruct{animalabilities[]string}func(animalanimal)setName(namestring){animal.name=name}func(animalanimal)getName()string{returnanimal.name}func(animalanimal)setFeet(feetint){animal.feet=feet}func(animalanimal)getFeet()int{returnanimal.feet}func(animalanimal)setHasPaws(hasPawsbool){animal.hasPaws=hasPaws}func(animalanimal)getHasPaws()bool{returnanimal.hasPaws}func(animalanimal)print(){fmt.Println(animal.getName(),"has",animal.getFeet(),"feet.")ifanimal.hasPaws{fmt.Println(animal.getName(),"has paws.")}}func(monstermonster)print(){fmt.Println(monster.getName(),"is a monster!!!")fmt.Println(monster.getName(),"has:")for_,ability:=rangemonster.abilities{fmt.Println("-",ability)}}funcmain(){vardragonproperties=monster{animal{"Dragon",4,true},[]string{"Nuclear blast","Ice smoke"},}fmt.Println(dragon.getName())}
I refactored the code first. I created an interface that contains getter and setter for the properties which is best practices I think. So, we don't need to access the properties directly to set and get the value.
The dragon now has an interface of properties. Try to remove a method that contained in properties, we will get an error. Those methods now are a must.
...// Monster is a special form of animals.
typeMonsterstruct{animalabilities[]string}...func(monsterMonster)print(){fmt.Println(monster.getName(),"is a monster!!!")fmt.Println(monster.getName(),"has:")for_,ability:=rangemonster.abilities{fmt.Println("-",ability)}}// Init initializes a monster.
funcInit(namestring,feetint,hasPawsbool,abilities[]string)*Monster{return&Monster{animal{name,feet,hasPaws},abilities,}}funcmain(){vardragonproperties=Init("Dragon",4,true,[]string{"Nuclear blast","Ice smoke"})fmt.Println(dragon.getName())}
I changed the monster to Monster (with uppercase at the first letter) and created the Init function as a constructor.
It looks like we missed something if we don't cover about constructor in OOP. Constructor is a function or method to set some values for the object. In other programming languages, mostly the constructor has the same name as the class or object. That was how we implemented the constructor in Go.
packagemainimport"fmt"typepropertiesinterface{setName(namestring)getName()stringsetFeet(feetint)getFeet()intsetHasPaws(hasPawsbool)getHasPaws()boolPrint()}// Animal has a name and feet. Maybe has paws too.
typeAnimalstruct{namestringfeetinthasPawsbool}// Monster is a special form of animals.
typeMonsterstruct{Animalabilities[]string}func(animalAnimal)setName(namestring){animal.name=name}func(animalAnimal)getName()string{returnanimal.name}func(animalAnimal)setFeet(feetint){animal.feet=feet}func(animalAnimal)getFeet()int{returnanimal.feet}func(animalAnimal)setHasPaws(hasPawsbool){animal.hasPaws=hasPaws}func(animalAnimal)getHasPaws()bool{returnanimal.hasPaws}// Print all properties of an animal.
func(animalAnimal)Print(){fmt.Println(animal.getName(),"has",animal.getFeet(),"feet.")ifanimal.hasPaws{fmt.Println(animal.getName(),"has paws.")}}// Print all abilities of a monster.
func(monsterMonster)Print(){fmt.Println(monster.getName(),"is a monster!!!")fmt.Println(monster.getName(),"has:")for_,ability:=rangemonster.abilities{fmt.Println("-",ability)}}// Init initializes a monster.
funcInit(namestring,feetint,hasPawsbool,abilities[]string)*Monster{return&Monster{Animal{name,feet,hasPaws},abilities,}}funcmain(){vardragonproperties=Init("Dragon",4,true,[]string{"Nuclear blast","Ice smoke"})dragon.Print()}
That was the final code. There are access modifiers such as default, private, protected, and public in other programming languages such as C/C++, Java, PHP, etc. There are only two in Go; private and public. They are contained in the name. The private starts with lowercase and the public starts with uppercase. It is cool to put comments on the public methods.
Closing
The structs or objects are public. The properties and methods are private. The only public methods are Print and Init which is a constructor. It is cool I think. Maybe we will learn about exporting objects or modules in my next article later. You can find the source code at https://github.com/aristorinjuang/go-oop.