DEV Community

Zaffere
Zaffere

Posted on

2 1

Golang Functional Options Pattern

The Functional Options Pattern is Creational Design Pattern which allows you to build complex structs using a constructor which takes in zero or more functions as arguments.

Sometimes we find ourselves having to deal with a domain object that takes in optional fields:

type Student struct {
  Name string
  Age int
  Address *string // optional
  PhoneNumber *int // optional
}

func NewStudent(name string, address string, age int, phoneNumber int)
Enter fullscreen mode Exit fullscreen mode

If all fields are required, instantiating a new student each time would be neat and easy to read:

student := NewStudent(name string, address string, age int, phoneNumber int)
Enter fullscreen mode Exit fullscreen mode

Even if we needed 10x students, using the constructor method as above would be we all well too:

student1 := NewStudent("Jon", "Kings Road", 12, 12345)
student2 := NewStudent("Tim", "Sixth Ave", 12, 11223)
student3 := NewStudent("Sara", "Orchard Road", 12, 22112)
student4 := NewStudent("Vanessa", "River Valley", 13, 43321)
student5 := NewStudent("Steve", "Killiney Road", 12, 55423)
Enter fullscreen mode Exit fullscreen mode

However, if we had 100 students new students and some provided their address and phone numbers while some don't, creating new students would get real messy real fast:
(With only 4 fields in the struct, this doesn't seem like a real problem however, in production, your domain model typically consists of more than just 4 fields which makes it easier to make mistakes and harder to read)

student1 := NewStudent("Jon", "River Valley", 12, 0)
student2 := NewStudent("Alex", "", 10, 22222)
student3 := NewStudent("Jones", "Kings Road", 12, 0)
student4 := NewStudent("Rachel", "", 12, 0)
student5 := NewStudent("Esther", "Killiney Road", 13, 55555)
...
Enter fullscreen mode Exit fullscreen mode

To help with this, we can use whats called an Optional method/function pattern

  1. Create a custom Type of type:function that takes in a pointer to the struct you want to build
  2. Expose a method that takes in those optional values and returns a function with the same function signature of the custom function type, this returned function of type:Option function would be responsible for adding those optional values to the struct
// Custom Type which is a function that takes in a pointer of the Student struct
type OptionalStudentValues func(s *Student)

func NewStudent(name string, age int, opts ...OptionalStudentValues) *Student {
    student := &Student{
        Name: name,
        Age:  age,
    }

    // Checks id there are optional values needed to be passed to the new Student struct
    if len(opts) > 0 {
        for _, opt := range opts {
            // execute the function retruned by the exposed methods, passing in the student struct
            opt(student)
        }
    }
    return student
}

// Implementation of the Optional function
func WithAddress(addr string) OptionalStudentValues {
    return func(s *Student) {
        s.Address = &addr
    }
}

func WithPhoneNumber(num int) OptionalStudentValues {
    return func(s *Student) {
        s.PhoneNumber = &num
    }
}

func main() {
    studentAddr := "Address string"
    studentPhoneNumber := 12345

    //  With optional fields
    s := NewStudent("Tim", 20, WithAddress(studentAddr), WithPhoneNumber(studentPhoneNumber))
    // No optional fields ...
    s2 := NewStudent("Mary", 12)

    log.Printf("This is the newly created student with optional fields : %+v", s)
    log.Printf("This is the newly created student with no optional fields : %+v", s2)
}
Enter fullscreen mode Exit fullscreen mode

Hot sauce if you're wrong - web dev trivia for staff engineers

Hot sauce if you're wrong ยท web dev trivia for staff engineers (Chris vs Jeremy, Leet Heat S1.E4)

  • Shipping Fast: Test your knowledge of deployment strategies and techniques
  • Authentication: Prove you know your OAuth from your JWT
  • CSS: Demonstrate your styling expertise under pressure
  • Acronyms: Decode the alphabet soup of web development
  • Accessibility: Show your commitment to building for everyone

Contestants must answer rapid-fire questions across the full stack of modern web development. Get it right, earn points. Get it wrong? The spice level goes up!

Watch Video ๐ŸŒถ๏ธ๐Ÿ”ฅ

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

๐Ÿ‘‹ Kindness is contagious

Please leave a โค๏ธ or a friendly comment on this post if you found it helpful!

Okay