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
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!
0 Nhận xét