DEV Community

loading...
Cover image for Functional Programming Design Patterns: Part 2 - Factory Method Pattern

Functional Programming Design Patterns: Part 2 - Factory Method Pattern

qgabe profile image Gabriel Ong ・5 min read

This post is Part 2 of a series on utilizing Design Patterns in the context of Functional Programming. If you are interested in learning more, stay tuned for more on my DEV page.

Introduction

As mentioned in the first part of the series, design patterns in Software Engineering (SE) have been widely popularized by the book Design Patterns: Elements of Reusable Object-Oriented Software written by the Gang of Four (GoF).

The book further categorizes design patterns into what kind of problems these patterns are designed to solve, namely the following categories:

  • Creational - Patterns that provide ways to instantiate single or groups of objects.
  • Structural - Patterns that provide ways to define relationships between classes and objects.
  • Behavioural - Patterns that define manners of communication between classes and objects.

Previously, we looked at the Strategy Pattern, which is a Behavioural pattern.

You can learn more about the categorization of the different patterns using this excellent page by Vince Huston.


Vince Huston's Diagram on Design PatternsVince's Periodic Table inspired diagram on Design Patterns.
It's interactive too!

For this article, we'll take a look at a Creational pattern: the Factory Method pattern.


The Factory Method Pattern

The Factory Method design pattern describes exposing a factory method in a Creator class without specifying the exact Product class that will be created. The factory method lets the Creator class defer instantiation of Products to its subclasses.

Factory Method Pattern

Whew! Once again, what a mouthful.

Let's break this definition down like we did last time. We have a few key components here:

  • Creator - An interface or abstract class that defines a factory method which returns a Product.
  • Product - An interface or abstract class that provides an abstract layer of reference to a concrete product.
  • Factory Method - A method that is implemented by concrete creators, which specific a concrete product to return.

If this is confusing, do not fret! This is easier to visualize with some examples!


Retail Store Example

In the spirit of the previous article, let us use the same Retail Store example but in a different problem context.

Suppose that for the retail store, we want to introduce a simulator to better estimate our business. In our simulator, we have two different "customer modes" we can select, where we can simulate a really bad and picky customers, or simulate a really good and gracious customers.

We want to differentiate the way we deal with creating the different Customers but without affecting the rest of the simulator code. We can implement the Factory Method pattern as follows:

interface CustomerCreator {
    public Customer createCustomer();
}

interface Customer {
    public boolean isGood();
}

class GoodCustomerCreator implements CustomerCreator {
    public Customer createCustomer() {
        return new GoodCustomer();
    }
}

class BadCustomerCreator implements CustomerCreator {
    public Customer createCustomer() {
        return new BadCustomer();
    }
}

class GoodCustomer implements Customer {
    public boolean isGood() {
        return true;
    }
}

class BadCustomer implements Customer {
    public boolean isGood() {
        return false;
    }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we can easily decouple the logic of having different ways to create and represent products. This pattern really shines when:

  1. There are an increasing number of different ways of create products. Let say for example, in our store simulator, we now want a way that will randomly return us a Good or Bad customer. We can simply create a RandomCustomerCreator and encapsulate the randomizing logic there. Our parent process will still only have to maintain a reference to a Creator class.
  2. There are an increasing number of products. Similarly, we only need to implement the Customer interface for any new customers and implement any additional logic there.

The Functional Approach

Let's see Strategy Pattern implemented in a Functional Programming-friendly way.

Currying

For this pattern, we can take advantage of a concept known as Currying. Currying is the concept mostly seen in functional programming and mathematics as the process where a function taking multiple arguments can be transformed into a function that takes in only a single argument while returning another function which accepts further arguments.

Another wordy definition! Let us illustrate this with a simple example:

Let us implement a function to add three numbers together:

const addMyTriple = (a, b, c) => {
    return a + b + c;
}

addMyTriple(1, 2, 3) // Returns 6.
Enter fullscreen mode Exit fullscreen mode

Currying this function would give us the following:

const addMyTriple = (a) => {
    return (b) => {
        return (c) => {
            return a + b + c;
        }
    }
}
addMyTriple(1)(2)(3) // Returns 6.
Enter fullscreen mode Exit fullscreen mode

This looks like a we're just increasing the verbosity of our function doesn't it? But the key is in realizing that we're returning the inner function every time. Suddenly, this becomes invaluable as we can fix variables and form other functions by using currying.

Here's an example. Let's say we want to add three numbers like before, but now we want to fix the first number to always be 10.

We can simply compose a new function by doing this:

const addMyDoublePlusTen = addMyTriple(10);

addMyDoublePlusTen(2,3) // Returns 15.
Enter fullscreen mode Exit fullscreen mode

Now let us apply this in implementing the Factory Method Pattern.

const customerCreator = (isGood) => {
    return isGood ? goodCustomer : badCustomer;
}

const customer = (isGood) => {
    return {
        isGood: isGood
    };
}

const goodCustomerCreator = () => {
    return customerCreator(true);
}

const badCustomerCreator = () => {
    return customerCreator(false);
}

const goodCustomer = () => {
    return customer(true);
}

const badCustomer = () => {
    return customer(false);
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we utilize currying here to differentiate the different creator functions by their type. We do that similarly for the customers themselves.

While this example seems intuitive, the key to understanding when to use the pattern is in two key nuances:

  1. How do you want to your instantiate objects? (Order, Composition, Configurations, etc.)
  2. How to represent the different types of objects? (Data Structure, etc.)

You might realize that this pattern maintains encapsulation of logic really nicely, even as the complexity of the example grows (which ties in with the whole point of using design patterns)!

Extending the example

To catalyze your learning process, think about some of the following questions and how you would implement them:

  • Suppose in our simulator we maintain a list of 10 Customers. How would you implement the Creators for a good day and a bad day?
  • What if there were instead 3 types of Customer? (Good, Bad, Neutral)

What's next

This post is Part 2 of a series on utilizing Design Patterns in the context of Functional Programming. If you are interested in learning more, stay tuned for more on my DEV page.

Discussion

pic
Editor guide