DEV Community

Cover image for Factory Design Pattern in Go
Tomas Sirio
Tomas Sirio

Posted on

Factory Design Pattern in Go

Hey there!

I've been studying a little bit on design patterns in Go since it's the last language that I've been learning.

So I think that the best way to learn as much as possible is to write about it in my own words. So without further ado, here is an example of Factory Functions, Factory Generators, and Prototype Factory.

Factory Functions

First, we are going to build our Post struct and a PostFactory

type Post struct {
    Content, Website, Author string
}

type PostFactory struct {
    Website, Author string
}
Enter fullscreen mode Exit fullscreen mode

Now we are going to create a function which in turn will return another function. This way we are going to have multiple types of factories to create different types of objects

func NewPostFactory(website string, author string) func(content string) *Post {
    return func(content string) *Post {
        return &Post{content, website, author}
    }
}
Enter fullscreen mode Exit fullscreen mode

This function receives two strings as parameters and returns a function that receives another string and finally returns a pointer to a Post. Let's see how this works on the main function.

Let's assume I want to write a Post and cross-post it to Dev.to, Hackernews.com and medium.com

func main() {
    devPostFactory := NewPostFactory("DEV.to", "Tomassirio")
    mediumPostFactory := NewPostFactory("Medium.com", "Tomassirio")
    hackerNewsPostFactory := NewPostFactory("Hackernews.com", "Tomassirio")

    content := "This is what I'm Cross-Posting today!"

    devPost := devPostFactory(content)
    mediumPost := mediumPostFactory(content)
    hackerNewsPost := hackerNewsPostFactory(content)

    fmt.Println(devPost)
    fmt.Println(mediumPost)
    fmt.Println(hackerNewsPost)
}
Enter fullscreen mode Exit fullscreen mode

In the first three lines, we are initializing the PostFactory's we are going to use to create our cross-posts. As you can see after creating the brief post, we use these factories as functions that return us the Posts.

go run factoriesDev.go                                                             
&{This is what I'm Cross-Posting today! DEV.to Tomassirio}
&{This is what I'm Cross-Posting today! Medium.com Tomassirio}
&{This is what I'm Cross-Posting today! Hackernews.com Tomassirio}
Enter fullscreen mode Exit fullscreen mode

Factory Generators

We are using the same structs examples as before. However, this time we are declaring a CreatePost function with a PostFactory as a receiver.

func NewPostFactory(website string, author string) *PostFactory {
    return &PostFactory{website, author}
}

func (f *PostFactory) CreatePost(content string) *Post {
    return &Post{content, f.Website, f.Author}
}
Enter fullscreen mode Exit fullscreen mode

This method is a little bit more versatile than the function factory since I can keep adding functions with a PostFactory as a receiver.

func main() {
    devPostFactory := NewPostFactory("DEV.to", "Tomassirio")
    mediumPostFactory := NewPostFactory("Medium.com", "Tomassirio")
    hackerNewsPostFactory := NewPostFactory("Hackernews.com", "Tomassirio")

    content := "This is what I'm Cross-Posting today!"

    devPost := devPostFactory.CreatePost(content)
    mediumPost := mediumPostFactory.CreatePost(content)
    hackerNewsPost := hackerNewsPostFactory.CreatePost(content)

    fmt.Println(devPost)
    fmt.Println(mediumPost)
    fmt.Println(hackerNewsPost)
}
Enter fullscreen mode Exit fullscreen mode

Prototype Factory

I didn't really like this method but you have to learn things before you can dislike them.

In this example, we are defining a type Website, which is actually a string, and an enum below where we declare the Websites were I'll be posting this (wink!)

type Website string

const (
    DEV         Website = "Dev.to"
    MEDIUM              = "Medium.com"
    HACKER_NEWS         = "Hackernews.com"
)
Enter fullscreen mode Exit fullscreen mode

We then declare the function where we'll create the post. There's a default case where we create an empty Post.

func NewPost(website Website) *Post {
    author := "Tomassirio"
    switch website {
    case DEV:
        return &Post{"", string(website), author}
    case MEDIUM:
        return &Post{"", string(website), author}
    case HACKER_NEWS:
        return &Post{"", string(website), author}
    default:
        return &Post{"", "", ""}
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally, we apply these functions to the main function

func main() {
    content := "This is what I'm Cross-Posting today!"

    devPost := NewPost(DEV)
    mediumPost := NewPost(MEDIUM)
    hackerNewsPost := NewPost(HACKER_NEWS)

    devPost.Content = content
    mediumPost.Content = content
    hackerNewsPost.Content = content

    fmt.Println(devPost)
    fmt.Println(mediumPost)
    fmt.Println(hackerNewsPost)
}
Enter fullscreen mode Exit fullscreen mode

The results are going to be always the same.

I hope you liked this post, I'll be trying to create another one with the Design Patterns that I'm going to be learning in the next few days.

Happy Coding!

Top comments (2)

Collapse
 
andrewpillar profile image
Andrew Pillar

Factory functions do have their place in Go, however it's wise not to reach for an abstraction before you need it. This typically results in code that has needless indirection, and makes it harder for you, the developer, to read. When writing Go code I like to keep in mind the Go proverb "Clear is better than clever"[1].

From my experience, I've found factory functions in Go to be of use when I'm working with an interface that has multiple implementations and I need an easy way of bootstrapping the interface. Typically using factory functions for simple structs is a pointless thing to do.

Go encourages a closer relationship with the code and the data that flows through your program, this is something that should be embraced.

[1] - go-proverbs.github.io/

Collapse
 
tomassirio profile image
Tomas Sirio

This is great advice. Thanks!