Connection between Go's Features and Code Refactoring

In the Go programming language, struct types and interfaces provide powerful ways to build readable and maintainable code. By using Go's structural composition and defining clear interfaces, we can create codebases that are easy to understand and modify. Let's explore these concepts in action.

Applying Encapsulation for Better Code Organization

Encapsulation in Go is achieved through package-level visibility and methods attached to structs. In Go, package-level visibility is achieved by starting the variable and method names with a lowercase letter, making them unexported and accessible only within the same package. Instead of classes, Go uses structs to group related fields and methods, making code more organized.

Consider student information scattered within a program:

package main

import "fmt"

var studentName = "Alice"
var studentAge = 20
var studentGrade = 3.9

func DisplayStudentInfo() {
    fmt.Println("Student Name:", studentName)
    fmt.Println("Student Age:", studentAge)
    fmt.Println("Student Grade:", studentGrade)
}

func UpdateStudentGrade(newGrade float64) {
    studentGrade = newGrade
}

Encapsulation is achieved by grouping these fields in a struct and creating methods to operate on them:

package main

import "fmt"

type Student struct {
    name  string
    age   int
    grade float64
}

func NewStudent(name string, age int, grade float64) *Student {
    return &Student{name: name, age: age, grade: grade}
}

func (s *Student) DisplayInfo() {
    fmt.Println("Student Name:", s.name)
    fmt.Println("Student Age:", s.age)
    fmt.Println("Student Grade:", s.grade)
}

func (s *Student) UpdateGrade(newGrade float64) {
    s.grade = newGrade
}

By encapsulating student properties and methods within a Student struct, we enhance the code's readability and maintainability.

Utilizing Abstraction with Interfaces

Abstraction in Go is accomplished through interfaces, which define method signatures that any implementing type must fulfill.

Here's a simple function outside a struct to calculate a GPA:

package main

import "fmt"

func CalculateGpa(grades []string) float64 {
    totalPoints := 0
    gradePoints := map[string]int{"A": 4, "B": 3, "C": 2, "D": 1, "F": 0}
    for _, grade := range grades {
        totalPoints += gradePoints[grade]
    }
    return float64(totalPoints) / float64(len(grades))
}

func main() {
    grades := []string{"A", "B", "A", "C"}
    gpa := CalculateGpa(grades)
    fmt.Printf("GPA: %.2f\n", gpa)
}

We can integrate this calculation within the Student struct, exposing only necessary methods to the user:

package main

import "fmt"

type Student struct {
    name   string
    grades []string
}

func NewStudent(name string, grades []string) *Student {
    return &Student{name: name, grades: grades}
}

func (s *Student) CalculateGpa() float64 {
    totalPoints := 0
    gradePoints := map[string]int{"A": 4, "B": 3, "C": 2, "D": 1, "F": 0}
    for _, grade := range s.grades {
        totalPoints += gradePoints[grade]
    }
    return float64(totalPoints) / float64(len(s.grades))
}

func main() {
    student := NewStudent("Alice", []string{"A", "B", "A", "C"})
    fmt.Printf("GPA: %.2f\n", student.CalculateGpa())
}

The Student struct now abstracts the GPA calculation, simplifying interaction.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal