Responsive Advertisement

JavaScript #10: Factory design pattern in JavaScript

 

You've worked a lot with JavaScript. You feel that sometimes your code is duplicated quite a lot. You want to change this. Or you are looking to learn about design patterns in JavaScript. Welcome to the series about design patterns in JavaScript. Through this series of articles, I hope you can apply design patterns to your programs.

Knowledge you need to have before reading the article:

Method .call() in JavaScript

This keyword in JavaScript

This keyword - Part 1

This keyword - Part 2

Table of contents:

  • ·             What is design pattern?
  • ·             What is Factory design pattern?
  • ·          Practical application

Let’s go to the content!


What is design pattern?

Design pattern is also your code. But when the code becomes too long, it is too difficult to manage, maintain or upgrade. Then they design design patterns to make the code cleaner, more manageable. And each design pattern will be suitable for specific situations. It is also possible to combine many types of design patterns together.

There are three main groups of design patterns: creational patterns, structural patterns and behavioral patterns.


What is factory design pattern?

Factory Method is a creational design pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created. And allows the sub-classes to choose the type of objects to create.

To create a true Factory design pattern, you need an interface (super class) to sub-class implements (inherits). 1 function to create object.

Factory design pattern solve problem how to solve recurring design problems to design flexible and reusable object-orented software.


Practical application

Let's see the problem posed. You have an education management system. You need to manage teachers and students by creating the information of these two objects and saving them to DataBase. At the same time, we will create some methods that are common to both and specific to each object.

Let's look at the usual code as you handle this task.

function Teacher(id, name, age) {

    this.id = id

    this.name = name

    this.age = age

    this.role = 'teacher'

    this.greeting = function () {

        return `I'm ${this.name}, and I'm ${age} years old`

    }

    this.teach = function () {

        return `I'm a teacher and I'll teach`

    }

}

 

function Student(id, name, age) {

    this.id = id

    this.name = name

    this.age = age

    this.role = 'student'

    this.greeting = function () {

        return `I'm ${this.name}, and I'm ${age} years old`

    }

    this.learn = function () {

        return `I'm a student and I'll teach`

    }

}

 

const teacher1 = new Teacher('tc1', 'teacher 1', 30)

const teacher2 = new Teacher('tc2', 'teacher 2', 34)

const student1 = new Student('st1', 'student 1', 18)

const student2 = new Student('st2', 'student 2', 18)

 

First, you'll see that some fields are repeated like id, name, age and greeting method which can be used for both objects.

Now your system is well known and well known. You need to manage more people and sometimes you need to manage several thousand people.

And when there are about 1000 students and your job is to repeat the code over 1000 times. It just sounds horrible. Now try to apply factory design pattern to see what can work?

First, we need to create an interface (super class) to sub-class implements (inherits). Next, we see that both objects have id, name, and age in common so we will separate out a common object for both (super class). And use the .call() method so that two child objects can inherit (sub-class).

function User(id, name, age) {

    this.id = id

    this.name = name

    this.age = age

}

 

function Teacher(id, name, age) {

    User.call(this, id, name, age)

    this.role = 'teacher'

}

 

function Student(id, name, age) {

    User.call(this, id, name, age)

    this.role = 'student'

}

 

Here we have the User() object which is a super class. And object Teacher and object Student() are sub-classes inherit from super class User().

We have completed the first step of grouping common fields about an object. And for child objects to inherit those fields. Next we see that creating by new Teacher or new Student is quite complicated. So we will create a function to automate this work. If you initialize with new Object() as above, you will have to enter it manually. When you use functions you can automate the creation of objects. This is why you use functions to create objects.

function UserFactory() {

    this.create = (id, name, age, type) => {

        switch (type) {

            case 1:

                return new Teacher(id, name, age)

            case 2:

                return new Student(id, name, age)

            default:

                break

        }

    }

}

 

const userFactory = new UserFactory()

 

Factory construction has been completed. Now you will create some common functions for both objects. And some separate functions for each object.

function User(id, name, age) {

    this.id = id

    this.name = name

    this.age = age

    this.greeting = function () {

        return `My name's ${name} and I'm ${age} years old`

    }

}

 

This is a function that objects can use to greet people. Besides, we will create specific methods for objects.

function Teacher(id, name, age) {

    User.call(this, id, name, age)

    this.role = 'teacher'

    this.teach = function () {

        return `I'm a teacher and I'll teach`

    }

}

 

function Student(id, name, age) {

    User.call(this, id, name, age)

    this.role = 'student'

    this.learn = function () {

        return `I'm a student and I'll learn`

    }

}

 

We just saw the convenience here. For fields or methods that can be shared, we return the interface (super class) to let the sub-classes inherit. For sub-classes, when you want to create specific methods, it is completely possible. Moreover, sub-classes can also override the methods of the interface (super class).

Now let's create the objects through the pre-built factory. And create a place to store these objects.

const userFactory = new UserFactory()

const usersArray = []

 

usersArray.push(userFactory.create('tc1', 'teacher 1', '35', 1))

usersArray.push(userFactory.create('tc2', 'teacher 2', '36', 1))

usersArray.push(userFactory.create('st1', 'student 1', '18', 2))

usersArray.push(userFactory.create('st2', 'student 1', '18', 2))

 

We've just created four objects. Let's go through the usersArray to use the greeting() method and what the typical methods would look like?

usersArray.forEach((user) => {

    console.log(user.toString())

    if (user.role === 'teacher') {

        console.log(user.teach())

    } else if (user.role === 'student') {

        console.log(user.learn())

    }

})

/**

 * My name's teacher 1 and I'm 35 years old

 * I'm a teacher and I'll teach

 *

 * My name's teacher 2 and I'm 36 years old

 * I'm a teacher and I'll teach

 *

 * My name's student 1 and I'm 18 years old

 * I'm a student and I'll learn

 *

 * My name's student 2 and I'm 18 years old

 * I'm a student and I'll learn

 */

 

Awesome! Shared methods and specific methods are perfectly fine to use. Now you can build higher system and base-code like this.

At the same time, you can completely create multiple objects according to the built-in function constructors. Make support, maintenance, and upgrades easier. Also, if you're building a full-fledged program, you'll be able to create objects automatically by calling the create() function. No need to handle by hand as usual.

Pros

Avoid tight coupling between creator and the concreate code into one place in the program.

Single Responsibility Principle. You can move the product create code into one place in the program making the coding easy to support.

Open/closed principle. Introduce new types of products into the program without breaking existing client’s code.

Cons

More complecate because create more subclasses to implements Factory design pattern.

Conclusion

If you see your program doing the same job creating new objects over and over again. You can apply the Factory design pattern to handle the above problem. To implement the Factory design pattern, you need an interface (super class) for sub-classes to implement (inherit). And finally you need a function to do the object creation.

Thank you for joining with me! If you have any ideas, feel free to comment below. Have a good day!

Đăng nhận xét

0 Nhận xét