Designing Factory pattern in line with Solid Design Principles
If you are a software design pattern keen then I am sure you must have come across Factory pattern in many articles which is one of the most known and widely implemented Creational design pattern.
Writing Readable and Scalable code are the 2 important factors in building application. Code that is not easily scalable or breaks the existing production code base can play havoc dealing with later when further changes are implemented or need to be scaled further.
To ensure the application is easily scalable and clean worth readable to other developers we need to make sure our code is in line with the Solid Design Principles too. Hmm lets just first have a recap of different Solid Design Principles with one liners.
a) Single-responsibility Principle — A class (or a file) should be responsible for only a single functionality. Do not overload your class.
b) Open-Closed Principle — Our code (prod level)should be open for extension but closed for modification. that means do not modify the existing prod level code. It may break. Instead extend the functionality and add new one.
c) Liskov Substitution Principle — If a functionality works for the base class then it must work the same way for the derived class too when substituted.
d) Interface Segregation Principle — Construct an Interface with minimum functionality. Because different Classes implementing this Interface may not need all functionality of interface. So segregate the Interfaces with different functionalities.
e) Dependency Inversion Principle — Low level class should not have any dependency on high level class. For eg. if class A uses a particular data structure to store, the other classes should not have direct access to this data structure because going forward the data structure may change from one to other. Instead data structure should be accessible through a method of class A
So jumping back to Factory pattern, I have seen many articles depicting the Factory pattern as below.
class DataStructureFactory {
constructor(name) {
this.name = name;
if(name === 'queue') {
this.ds = new Queue();
}
else if(name === 'stack') {
this.ds = new Stack();
}
return this.ds;
}
}class Stack
{
constructor() {
this.data = [];
} add(value) {
this.data.push(value);
} remove() {
this.data.pop();
} print() {
console.log(this.data);
}
}class Queue
{
constructor() {
this.first = null;
this.last = null;
}
enqueue(value) {
let newNode = {
value,
next: null
}; if (this.first === null) {
this.first = {
value,
next: null
};
return this.last = this.first;
}
this.last.next = newNode;
this.last = newNode;
} dequeue() {
this.first = this.first.next;
} print() {
let values = []
let currentNode = this.first;
while (currentNode) {
values.push(currentNode.value);
currentNode = currentNode.next;
} console.log(values);
}
}
Do you see any harm in it? We will be intializing different data structures using DataStructureFactory
class. Like…
const queue = new DataStructureFactory('queue');
const stack = new DataStructureFactory('stack');// populating and unpopulating data structure with some valuesqueue.enqueue(5);
queue.enqueue(51);
queue.enqueue(70);
queue.dequeue();
stack.add(10);
stack.add(15);
stack.remove();//retrieving the data structure valuesstack.print();
queue.print();
The above way of implementing Factory pattern has some limitations. Like we are using if.. else to outsource object creation. What if going down the line there is a new data structure LinkedList
to be implemented. We will be writing one more if
statement to instantiate it. And then further if tens of data structure then tens of if..else
statements. And if instead of Data Structure it were something unlimited sort of thing then it would lead to state explosion. And secondly we will be violating one of the solid design principles — Open-Closed Principle which states that our code should be open for extension but closed for modification. And here we are trying to modify the method that is already working fine on production.
So we see our code is not really Scalabale and looks unclean with too many conditions. With the help of below code I would like to demonstrate how we can write Factory pattern that is Scalable, Looks clean and in-line with Design Principles.
class DataStructureFactory {
initStack() {
return new Stack();
} initQueue() {
return new Queue();
}
}class Stack
{
constructor() {
this.data = [];
} add(value) {
this.data.push(value);
} remove() {
this.data.pop();
} print() {
console.log(this.data);
}
}class Queue
{
constructor() {
this.first = null;
this.last = null;
} enqueue(value) {
let newNode = {
value,
next: null
}; if (this.first === null) {
this.first = {
value,
next: null
};
return this.last = this.first;
}
this.last.next = newNode;
this.last = newNode;
} dequeue() {
this.first = this.first.next;
} print() {
let values = []
let currentNode = this.first;
while (currentNode) {
values.push(currentNode.value);
currentNode = currentNode.next;
}
console.log(values);
}
}const queue = new DataStructureFactory().initQueue();
const stack = new DataStructureFactory().initStack();// populating and unpopulating data structure with some valuesqueue.enqueue(5);
queue.enqueue(51);
queue.enqueue(70);
queue.dequeue();
stack.add(10);
stack.add(15);
stack.remove();//retrieving the data structure valuesstack.print();
queue.print();
In the above code snippet class DataStructureFactory
consists of different methods to intialize different kinds of data structures. The class Stack
and class Queue
remain unchanged.
So comparing to our earlier class DataStructureFactory
our new class DataStructureFactory
is very much scalable, does not violate Solid Design Principles and looks clean where we have different methods for different data structure initialization and method name is self-explanatory. Going forward even if we have multiple data strucures we won’t have muliple conditions in one huge constructor cluttered up with if..else
statements.
Conclusion: We not only learnt how to design Factory pattern
but also how to design them clean, scalable and in line with Open-Closed principle where down the line we wont have to hit state explosion and end up with multiple conditions in our class constructor.
Hope you liked the piece of information and do let me know what you feel about it or would like to add something more to it.