avatarJacklyn Parrish

Summary

The provided content offers an in-depth exploration of Object-Oriented Programming (OOP) concepts in Go, including methods, interfaces, struct embedding, and generics, demonstrating how Go handles OOP differently from traditional OOP languages like Java or Python.

Abstract

The article delves into the nuances of OOP in Go, clarifying common misconceptions about Go's OOP capabilities. It explains how Go, despite not supporting classes in the traditional sense, allows for the creation of methods on types, which is akin to defining behavior for custom types. The concept of structs is introduced as a means to group related data, and methods are shown to provide object-oriented functionality to these structs. The use of pointer receivers in methods is discussed, emphasizing their necessity for modifying the receiver or handling large structs efficiently. The article also covers interfaces, describing them as types that define a set of methods, and illustrates how any type that implements these methods satisfies the interface. Struct embedding is presented as a form of composition, offering benefits similar to inheritance but with a simpler and safer implementation. Lastly, the article previews the upcoming feature of generics in Go 1.18, showcasing how they can create flexible and maintainable code by working with a range of types.

Opinions

  • The author suggests that Go's approach to OOP, which focuses on composing functionality and providing simple mechanisms, results in more readable and maintainable code compared to traditional OOP languages.
  • The article implies that Go's interfaces are more straightforward and less verbose than those in other languages, as types automatically implement an interface by providing the required methods without explicit declaration.
  • The author posits that struct embedding in Go, which allows for the combination of simple objects to create more complex ones, is preferable to inheritance due to its simplicity and safety.
  • The anticipation of generics in Go is highlighted as a significant enhancement that will increase the flexibility and maintainability of Go programs.

Go Basic: Understanding Methods, Interfaces, Struct Embedding, Generics: Covering OOP concepts in Go

Let’s dive into the exciting world of Object-Oriented Programming (OOP) in Go. 😄

This article was produced with the help of AI

Photo by Element5 Digital on Unsplash

full lessons here👇:

Topic: Introduction to Object-Oriented Programming in Go

Historically, Go has been represented as a language that does not support traditional object-oriented programming semantics. This, however, is not entirely accurate. In Go, we can’t create classes in the traditional sense used in languages like Java or Python, but we can define methods on types. This is Go’s way of letting us define behaviour for our own types, and this is comparable with the concept of a ‘class’ in other languages.

In essence, Go is more about composing functionality and providing only the simplest of mechanisms to do so. This simplicity results in readable and maintainable code that serves as a major advantage of Go over other object-oriented programming languages.

Remember, the aim of Object-Oriented Programming (OOP) is to design the program using data and functions as a single unit called an object. Similarly, with Go’s approach, we can also encapsulate data and functions into a single entity.

Topic: Structures in Go

Structures, or structs as we call them commonly in Go, are custom types that group together zero or more other types.

We define a new struct type using the type keyword, followed by the name of the struct, and then the keyword struct. The actual definition of the struct fields is contained within braces {}.

Here’s a basic example:

type Person struct {
    Name string
    Age  int
}

In this example, we’re defining a new type named Person with two fields: Name, which is a string, and Age which is an integer.

We can then create instances of our Person struct like so:

p := Person{"John Doe", 30}
fmt.Println(p.Name) // Output: John Doe
fmt.Println(p.Age)  // Output: 30

In Go, the . operator is used to access the fields of a struct instance. As you can see structs are very powerful for grouping related data together. It's essentially Go's approach to building classes like you would in other languages.

Topic: Methods in Go

In Go, a method is a function that is associated with a particular type. Methods on types are what provide the ‘object-oriented’ feel to the Go programming language, even though strictly speaking, Go does not have objects or classes. Here’s a simple demonstration how to declare a method in Go:

type Circle struct {
    Radius float64
}
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

In this example, we define a new type Circle and then we define a method Area() that operates on the type Circle.

c := Circle{10}
fmt.Println(c.Area()) // Outputs: 314.1592653589793

This above example is interesting because now we’re calling a function directly on an instance of Circle - this is similar behavior to calling methods on objects in languages like Java or Javascript.

let’s discuss pointer receivers in Go methods.

Topic: Pointer Receivers in Go

In Go, you can also declare methods with pointer receivers. This means the receiver type has the * operator before it. Here's an example:

type Employee struct {
    Name string
    Salary int
}
func (e *Employee) giveRaise(amount int) {
    e.Salary += amount
}

In this example, giveRaise() is a method with a pointer receiver of type Employee. Now, when we call giveRaise(), it will modify the original Employee variable, because it has a reference to the original item.

e := Employee{"John", 5000}
e.giveRaise(1000)
fmt.Println(e.Salary)  // Outputs: 6000

You may ask, why and when to use pointer receivers?

  1. If we need to modify the receiver, we should use a pointer receiver. Like in the giveRaise() example.
  2. If the struct is large, a pointer receiver will be more efficient, because it doesn’t need to copy the entire struct.

Topic: Interfaces in Go

An interface in Go is a type that simply defines a set of methods. If any other type implements these methods, then that type is considered to implement the interface. Here’s an example:

type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

In this code, we define an interface Shape that has a single method Area(). Then we define two different structs, Circle and Rectangle, each with an Area() method. Both Circle and Rectangle now implement the Shape interface. We can treat instances of Circle and Rectangle as Shape:

func printArea(s Shape) {
    fmt.Println(s.Area())
}
c := Circle{10}
r := Rectangle{10, 20}
printArea(c) // Outputs: 314.1592653589793
printArea(r) // Outputs: 200

Go interface is different than that in other languages. In Go, we don’t need to explicitly declare that a type implements an interface. If a type provides all methods required by an interface, it automatically implements that interface.

Topic: Struct Embedding and Composition in Go

In Go, we can include or ‘embed’ one struct inside another. This ‘embedding’ provides a lot of the benefits of inheritance, but it’s much simpler and safer. In terms of object-oriented concepts, it’s more like composition (combining simple objects to form more complex ones) rather than inheritance. Here’s an example:

type Person struct {
    Name string
    Age  int

type Employee struct {
    Person
    Position string
    Salary   int
}

In the above example, Employee struct embeds Person struct, meaning it includes all the fields and methods of the Person.

We can access the fields of the Person struct directly from Employee. In fact, it’s as if these fields exist directly on the Employee struct:

e := Employee{
    Person: Person{
        Name: "Bob",
        Age:  30,
    },
    Position: "Developer",
    Salary:   80000,
}
fmt.Println(e.Name)   // Outputs: Bob
fmt.Println(e.Age)    // Outputs: 30
fmt.Println(e.Salary) // Outputs: 80000

Topic: Generics in Go

Generics, which is to be introduced in Go 1.18, is one of the most awaited features in Go. Generics can make your programs more flexible and maintainable by representing a range of types, as opposed to a single one. Here is a simple example of how generics might look:

func PrintSlice[T any](s []T) {
  for _, v := range s {
    fmt.Println(v)
  }
}
func main() {
  PrintSlice[int]([]int{1, 2, 3, 4, 5})
  PrintSlice[string]([]string{"Hello", "World"})
}

In this code, PrintSlice[T any](s []T) is a generic function that works for a slice of any type. T is a type parameter- it stands for any type. The any keyword means that T could be any type at all: an int, float, string, etc., including user-defined types!

In the main function, we call PrintSlice with slices of int and string. This demonstrates that PrintSlice is a generic function.

this example gives you a basic understanding of how generics would work in Go.

Golang
Learning
Self Improvement
Programming
Golang Tutorial
Recommended from ReadMedium