Factory Method - Design Pattern
Creational: Factory Pattern
What is the Factory Pattern in JavaScript?
View Answer:
The objects participating in this pattern are:
Creator -- In example code: Factory
- the Factory object that creates new products
- implements factory method which returns newly created products
AbstractProduct -- not used in JavaScript
- declares an interface for products
ConcreteProduct -- In example code: Employees
- the manufactured product
- All products support the same interface (properties and methods)
Though the definition particularly mentions that an interface needs to be defined, we do not have interfaces in JavaScript. Therefore, we must implement it so that JavaScript translates into an interface.
In JavaScript, you can implement the Factory pattern like this.
function CarMaker() {}
CarMaker.prototype.drive = function() {
return `I am a ${this.type} and I can drive`;
}
// Static factory method
CarMaker.factory = function(type) {
let constr = type,
newCar;
// Check if the constructor exists
if(typeof CarMaker[constr] !== 'function') {
throw {
name: "Error",
message: `${constr} does not exist`
};
}
// At this point, the constructor is known to exist
// Let's have it inherit the parent but only once
if(typeof CarMaker[constr].prototype.drive !== 'function') {
CarMaker[constr].prototype = new CarMaker();
}
// Create a new instance
newCar = new CarMaker[constr]();
return newCar;
}
// Define specific car makers
CarMaker.Compact = function() {
this.type = 'Compact car';
};
CarMaker.Sedan = function() {
this.type = 'Sedan car';
};
CarMaker.Suv = function() {
this.type = 'SUV car';
};
// Usage:
let corolla = CarMaker.factory('Compact');
let camry = CarMaker.factory('Sedan');
let highlander = CarMaker.factory('Suv');
console.log(corolla.drive()); // I am a Compact car and I can drive
console.log(camry.drive()); // I am a Sedan car and I can drive
console.log(highlander.drive());// I am a SUV car and I can drive
In this example, the CarMaker
factory has a factory
method that creates a new object based on the type
argument. Each type of car is a function constructor, and they all inherit from CarMaker
.
In what pattern category does the factory pattern belong?
View Answer:
What is a factory method in JavaScript?
View Answer:
Here's an example of a factory method in JavaScript:
// Product interface
class Product {
constructor(name) {
this.name = name;
}
// ...
}
// Concrete products
class ConcreteProductA extends Product {
// ...
}
class ConcreteProductB extends Product {
// ...
}
// Factory
class Factory {
createProduct(type) {
switch (type) {
case 'A':
return new ConcreteProductA('Product A');
case 'B':
return new ConcreteProductB('Product B');
default:
throw new Error('Invalid product type.');
}
}
}
// Usage
const factory = new Factory();
const productA = factory.createProduct('A');
console.log(productA.name); // Output: Product A
const productB = factory.createProduct('B');
console.log(productB.name); // Output: Product B
In this example, the Factory
class provides a createProduct
method that takes a type parameter and returns a new instance of the appropriate ConcreteProduct
based on the type provided. The factory encapsulates the creation logic, allowing the client code to create products without being aware of the specific implementation details.
What is the primary goal of the factory method?
View Answer:
What are the object participants in the factory pattern?
View Answer:
The Factory pattern involves several key participants:
- The
Product
(Interface for creating objects) - The
ConcreteProduct
(Implements theProduct
interface) - The
Creator
(Declares the factory method, which returns an object ofProduct
type) - The
ConcreteCreator
(Overrides the factory method to return an instance of aConcreteProduct
)
Here is an example in JavaScript:
// Product
class Shape {
constructor() { }
draw() { }
}
// ConcreteProduct
class Circle extends Shape {
constructor() {
super();
}
draw() {
console.log("Drawing a Circle");
}
}
// ConcreteProduct
class Square extends Shape {
constructor() {
super();
}
draw() {
console.log("Drawing a Square");
}
}
// Creator
class ShapeFactory {
constructor() { }
createShape(type) {
switch (type) {
case 'circle':
return new Circle();
case 'square':
return new Square();
default:
return null;
}
}
}
// Let's use our factory to create objects
const shapeFactory = new ShapeFactory();
let shape1 = shapeFactory.createShape('circle');
shape1.draw(); // "Drawing a Circle"
let shape2 = shapeFactory.createShape('square');
shape2.draw(); // "Drawing a Square"
In this example, Shape
is the Product
interface, Circle
and Square
are ConcreteProduct
implementing the Shape
interface. ShapeFactory
is the Creator
that contains the createShape
factory method for creating objects of the Shape
type. The factory method returns an instance of the required ConcreteProduct
(Circle or Square), depending on the input parameter.
Can you name a use case for the factory pattern?
View Answer:
You should use the Factory Method when you don’t know the exact types and dependencies of the objects your code should work within your application.
When you want to give users of your library or framework the ability to extend its internal components, we use the Factory Method to meet this specification.
The Factory Method may be used to save system resources by reusing existing objects rather than constructing them each time.
When should you use the Factory Pattern in JavaScript?
View Answer:
class Enemy {
constructor(name, speed, health, power, attackType) {
this.name = name;
this.speed = speed;
this.health = health;
this.power = power;
this.attackType = attackType;
}
attack() {
return `${this.name} attacks with ${this.attackType}`;
}
defend(damage) {
this.health -= damage;
if (this.health <= 0) {
return `${this.name} is defeated`;
}
return `${this.name} has ${this.health} health remaining`;
}
}
class EnemyFactory {
createEnemy(type) {
switch(type) {
case 'warrior':
return new Enemy('Warrior', 3, 100, 10, 'sword');
case 'archer':
return new Enemy('Archer', 5, 75, 7, 'bow');
case 'mage':
return new Enemy('Mage', 1, 50, 25, 'magic');
default:
throw new Error('Invalid enemy type');
}
}
}
const enemyFactory = new EnemyFactory();
const enemies = [
enemyFactory.createEnemy('warrior'),
enemyFactory.createEnemy('archer'),
enemyFactory.createEnemy('mage')
];
enemies.forEach(enemy => {
console.log(enemy.attack());
console.log(enemy.defend(20));
});
In this example, we've abstracted the creation of different enemy types into a Factory. This way, the main code doesn't need to know the details about how to create each type of enemy. If we need to add more enemy types in the future, we can do so easily by modifying the factory, without touching the rest of the code.
What are the benefits of using the Factory Pattern?
View Answer:
Benefits of the Factory Pattern.
- You avoid a close relationship between the Creator and the concrete products.
- Single Responsibility Principle (S.R.P.): The principle of single responsibility. You can consolidate the product creation code into a single location in the program, making it easier to support.
- Open/Closed Principle: You can integrate new products into the system without busting the current client code.
What are some of the factory pattern's drawbacks?
View Answer:
Can you name some alternatives to the factory pattern?
View Answer:
Here is a list of alternatives to the Factory Pattern:
- Object literals
- Constructor functions
- Dependency injection
- Abstract factory pattern
- Builder pattern
- Singleton pattern
- Prototype pattern
- Module pattern
These alternatives provide various ways to handle object creation and instantiation in JavaScript, each with its own benefits and use cases.
How is the Factory Pattern different from the Constructor Pattern in JavaScript?
View Answer:
Is the Factory Pattern the best pattern for creating a large number of identical objects?
View Answer:
How can the Factory Pattern help with encapsulation?
View Answer:
What is a Simple Factory Pattern?
View Answer:
Here's an example of the Simple Factory pattern in JavaScript.
class Car {
constructor(model, doors, color) {
this.model = model;
this.doors = doors;
this.color = color;
}
}
class CarFactory {
createCar(type) {
switch(type) {
case 'sedan':
return new Car('Sedan', 4, 'black');
case 'coupe':
return new Car('Coupe', 2, 'red');
case 'suv':
return new Car('SUV', 5, 'blue');
default:
return null;
}
}
}
// Usage
const carFactory = new CarFactory();
const sedan = carFactory.createCar('sedan');
console.log(sedan); // Car { model: 'Sedan', doors: 4, color: 'black' }
const coupe = carFactory.createCar('coupe');
console.log(coupe); // Car { model: 'Coupe', doors: 2, color: 'red' }
const suv = carFactory.createCar('suv');
console.log(suv); // Car { model: 'SUV', doors: 5, color: 'blue' }
In this example, the CarFactory
class has a createCar
method that creates a new Car
object based on the type
argument. The Car
class, which is the object being created by the factory, takes model
, doors
, and color
as arguments to its constructor. This is a simpler implementation than a full Factory pattern, but it achieves a similar result: it provides a way to delegate the creation of objects to a specific method, which can be modified to change the creation behavior.
Can you name a real-world example where the Factory Pattern is useful?
View Answer:
Here's an example using Node.js...
class DbConnection {
constructor() {}
connect() {
throw new Error("This method must be overwritten!");
}
disconnect() {
throw new Error("This method must be overwritten!");
}
}
class MySqlConnection extends DbConnection {
constructor() {
super();
}
connect() {
console.log("Connecting to MySQL...");
// Here you would have code that sets up a connection to MySQL
}
disconnect() {
console.log("Disconnecting from MySQL...");
// Code to disconnect from MySQL
}
}
class PostgresConnection extends DbConnection {
constructor() {
super();
}
connect() {
console.log("Connecting to PostgreSQL...");
// Here you would have code that sets up a connection to PostgreSQL
}
disconnect() {
console.log("Disconnecting from PostgreSQL...");
// Code to disconnect from PostgreSQL
}
}
class DbConnectionFactory {
createDbConnection(type) {
switch(type) {
case 'mysql':
return new MySqlConnection();
case 'postgresql':
return new PostgresConnection();
default:
throw new Error(`Database type ${type} not supported.`);
}
}
}
// Usage
const factory = new DbConnectionFactory();
const dbConnection = factory.createDbConnection('mysql'); // Depending on the environment or user selection, you might choose 'postgresql'
dbConnection.connect(); // Connecting to MySQL...
// Somewhere later in your code when you're done with the connection
dbConnection.disconnect(); // Disconnecting from MySQL...
In this example, the DbConnectionFactory
class serves as a Factory for creating different types of DbConnection
instances. The specific type of DbConnection
that gets instantiated depends on the string that's passed into the createDbConnection
method. This can easily be extended to support other types of databases as well, you would just need to create a new class for that database type and add another case in the switch
statement.
How does the Factory Pattern contribute to the SOLID principles?
View Answer:
What is the difference between a Factory Method and the Factory Pattern?
View Answer:
Here are simple code examples for both patterns in JavaScript:
- Factory Method:
class PizzaFactory {
createPizza(type) { // createPizza is a Factory Method
if (type === 'cheese') {
return new CheesePizza();
} else if (type === 'pepperoni') {
return new PepperoniPizza();
}
}
}
In this example, createPizza
is a Factory Method. It creates different types of Pizza objects based on the input type
.
- Factory Pattern:
class PizzaFactory {
static createCheesePizza() {
return new CheesePizza();
}
static createPepperoniPizza() {
return new PepperoniPizza();
}
}
In this Factory Pattern example, we have a PizzaFactory
class with two static methods: createCheesePizza
and createPepperoniPizza
. Each of these methods creates a specific type of Pizza object.
What is the modern approach used in the factory pattern, Classes or Functions?
View Answer:
The examples below will demonstrate both approaches.
Factory Function Approach:
const carFactory = (type) => {
switch(type) {
case 'Compact':
return {
doors: 4,
drive: () => 'Vroom, I have 4 doors'
};
case 'Convertible':
return {
doors: 2,
drive: () => 'Vroom, I have 2 doors'
};
case 'SUV':
return {
doors: 24,
drive: () => 'Vroom, I have 24 doors'
};
default:
throw new Error('Invalid car type');
}
};
const corolla = carFactory('Compact');
console.log(corolla.drive()); // "Vroom, I have 4 doors"
Class Approach:
class Car {
constructor(doors) {
this.doors = doors;
}
drive() {
return `Vroom, I have ${this.doors} doors`;
}
}
class CompactCar extends Car {
constructor() {
super(4);
}
}
class ConvertibleCar extends Car {
constructor() {
super(2);
}
}
class SUVCars extends Car {
constructor() {
super(24);
}
}
class CarFactory {
createCar(type) {
switch(type) {
case 'Compact':
return new CompactCar();
case 'Convertible':
return new ConvertibleCar();
case 'SUV':
return new SUVCars();
default:
throw new Error('Invalid car type');
}
}
}
const carFactory = new CarFactory();
const corolla = carFactory.createCar('Compact');
console.log(corolla.drive()); // "Vroom, I have 4 doors"
In this example, we first define a base Car
class and then subclasses for specific types of cars. Then we define a CarFactory
class that creates an instance of the appropriate subclass based on the given type.
Both these approaches can be used in modern JavaScript to implement the Factory pattern. The class approach is more structured and can be more suitable if there are many types of cars, or if the logic to construct a car is complex. On the other hand, the factory function approach is more flexible and less verbose for simpler cases.