• Register

Factory Design Pattern in Go

posted Nov 18

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
}

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}
    }
}
 

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](https://dev.to/tomassirio), [Hackernews.com](https://news.ycombinator.com/) and [medium.com](https://tomassirio.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)
}

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}
 

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}
}

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)
}

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"
)

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{"", "", ""}
    }
}

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)
}

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!

0 points