Sunday, April 28, 2024

Mastering Go: Part 3 - Programming paradigm(OOP and procedural)

 

GO programming paradigm

Go mostly follows the procedural language paradigm and supports coding following the OOP concept, utilizing types and methods, even though Go is not a pure OOP language. Go doesn't provide a type hierarchy like pure OOP languages to create objects. Since it supports multiple programming paradigms, it is often referred to as a post-OOP or concurrent programming language.

Interfaces

Go doesn’t have the concept of classes and inheritance. Instead, it uses interface types to define method signatures for common types. The 'interface' keyword is utilized to create an interface.

type interface_name interface {
// method signatures
}

To implement an interface, all methods of the interface type must be implemented in the derived type. In Go, interfaces are implemented implicitly, unlike in languages such as C# and Java.
For example, let's continue the exercise from Part 2 of this series Mastering Go: Part 2 - Structuring Your Go Projects
Step 1. Create a new file named formatter.go in the string_formatter package and add the following code:
package stringformater

// formater interface to format the strings
type Formater interface {
Format() string
}

Step 2. Update the main function and add the following code:

package main

import (
"fmt"
dateformater "learngo/date_formater" // import data formater package
stringformater "learngo/string_formater" // import string formater package
)

func main() {

fmt.Println("Import date format package!")
dateformater := dateformater.Date{Year: "2021", Month: "07", Day: "01"}
fmt.Println(dateformater.Format())

//Create an instance of an interface 'Formater'
var istringFormater stringformater.Formater

fmt.Println("Use name format package to format name!")
// Create the instance if PersonName struct and assign to string formater interface
istringFormater = &stringformater.PersonName{FirstName: "Sharad", LastName: "Subedi"}
fmt.Println(istringFormater.Format())

fmt.Println("Use name format package to format billing address!")
//create the instance if Address struct and assign to string formater interface
istringFormater = &stringformater.Address{StreetNumber: "123", StreetName: "Main St", City: "San Francisco", State: "CA", Zip: "94101"}
fmt.Println(istringFormater.Format())

}


In the above code, both the PersonName and Address structs implement all the methods defined in the Formatter interface. Since the interface type (Formatter) and the types (PersonName and Address) implement the interface in the same package (string_formatter), the interface is implicitly implemented for these types.

Go supports an empty interface, which is an interface with zero methods. This means that all types implicitly implement this interface.
Additionally, Go allows the use of multiple interfaces. One interface can implement another interface as well.

Polymorphism

In Go, there is no concept of classes and objects. Instead, we utilize Go interfaces to achieve polymorphism. When we create a variable of an interface type in Go, it can hold any other types that implement the interface. This feature allows us to achieve polymorphism effectively.

Example: Continue the exercise from the previous step (interface).

Step 1. Modify the main method and add the function formatString with the parameter as the interface type.
Step 2. Call the function formatString for PersonName and Address struct types.
Update the main method with the code below and observe the function formatString and its uses.

package main

import (
"fmt"
dateformater "learngo/date_formater" // import data formater package
stringformater "learngo/string_formater" // import string formater package
)

func main() {

fmt.Println("Import date format package!")
dateformater := dateformater.Date{Year: "2021", Month: "07", Day: "01"}
fmt.Println(dateformater.Format())

fmt.Println("Use name format package to format name!")
// Create the instance of PersonName struct
personName := &stringformater.PersonName{FirstName: "Sharad", LastName: "Subedi"}
fmt.Println(formatString(personName))

fmt.Println("Use name format package to format billing address!")
//create the instance of Address struct
address := &stringformater.Address{StreetNumber: "123", StreetName: "Main St", City: "San Francisco", State: "CA", Zip: "94101"}
fmt.Println(formatString(address))
}

// function to format the strings. Takes the interface type as input.
// Example to implement polymorphism using interface
func formatString(formater stringformater.Formater) string {

return formater.Format()
}


Encapsulation

In Go, structs and methods are utilized to encapsulate data. If the properties and/or methods of a struct begin with a capital letter, they are considered public and accessible from outside the package. Conversely, if they start with a lowercase letter, they are considered private and accessible only within the same package.

Constructor

Declare a function to do some initialization or validity check for the type. the constructor will always execute if the method from the type is accessed.  

Example: Continuing from the previous section(Polymorphism)

Step 1. Modify the date_formater file and add a new constructor to the struct Date. Copy the code below:
package dateformater

type Date struct {
Year string
Month string
Day string
}

func NewDate(year string, month string, day string) *Date {
return &Date{
Year: year,
Month: month,
Day: day,
}

}

func (d *Date) Format() string {
return d.Month + "/" + d.Day + "/" + d.Year
}


In the above code, the constructor simply returns an instance of the Date struct type.
Step 2. Modify the main.go file and create a new instance of the Date struct using its constructor. Copy the code below:

package main

import (
"fmt"
dateformater "learngo/date_formater" // import data formater package
stringformater "learngo/string_formater" // import string formater package
)

func main() {

fmt.Println("Crete new date struct type instance using a constructor!")
dateConstructor := dateformater.NewDate("2021", "07", "01")
fmt.Println(dateConstructor.Format())

// fmt.Println("Create the instance of Date using!")
// dateformater := dateformater.Date{Year: dateConstructor.Year, Month: dateConstructor.Month, Day: dateConstructor.Day}
// fmt.Println(dateformater.Format())

fmt.Println("Use name format package to format name!")
// Create the instance of PersonName struct
personName := &stringformater.PersonName{FirstName: "Sharad", LastName: "Subedi"}
fmt.Println(formatString(personName))

fmt.Println("Use name format package to format billing address!")
//create the instance of Address struct
address := &stringformater.Address{StreetNumber: "123", StreetName: "Main St", City: "San Francisco", State: "CA", Zip: "94101"}
fmt.Println(formatString(address))
}

// function to format the strings. Takes the interface type as input.
// Example to implement polymorphism using interface
func formatString(formater stringformater.Formater) string {

return formater.Format()
}



Reference

https://go.dev/learn/

1 comment:

Mastering Go: Part 14 - Messaging with Apache Kafka(Go Implementation)

In this post, we will explore how to implement Apache Kafka messaging in Golang. Several packages are available, and the best choice depends...