Basic design pattern examples in JavaScript.
The Builder pattern separates the construction of a complex object from its representation. This allows the same construction process to create different representations of the object.
// The Car class represents a car with various properties such as make, model, year, color, and engine. It uses a builder object to initialize these properties.
class Car {
constructor(builder) {
this.make = builder.make;
this.model = builder.model;
this.year = builder.year;
this.color = builder.color;
this.engine = builder.engine;
}
// Returns a string representation of the car
toString() {
return `${this.year} ${this.make} ${this.model} in ${this.color} with a ${this.engine} engine`;
}
}
The CarBuilder
class helps in building a Car
object step by step. It allows setting various properties of the car and then building the final Car
object.
// CarBuilder class helps in building a Car object step by step
class CarBuilder {
constructor(make, model) {
this.make = make;
this.model = model;
}
// Sets the year of the car
setYear(year) {
this.year = year;
return this;
}
// Sets the color of the car
setColor(color) {
this.color = color;
return this;
}
// Sets the engine type of the car
setEngine(engine) {
this.engine = engine;
return this;
}
// Builds and returns a Car object
build() {
return new Car(this);
}
}
This example demonstrates how to use the CarBuilder
class to create a Car
object.
// Usage example
const car = new CarBuilder("Toyota", "Camry")
.setYear(2021) // Setting the year
.setColor("Red") // Setting the color
.setEngine("V6") // Setting the engine type
.build(); // Building the car
// Output the car details
console.log(car.toString());
- Fluent Interface: The
CarBuilder
class uses a fluent interface, allowing method chaining. Each setter method returns the builder object itself (this
), enabling chaining multiple method calls in a single statement. - Separation of Concerns: The builder pattern separates the construction of a complex object (
Car
) from its representation, allowing the same construction process to create various representations. - Immutability: Once a
Car
object is created, its properties are immutable (cannot be changed), ensuring the integrity of the object.
The Builder pattern is useful for constructing complex objects with many optional parameters.
The Facade pattern provides a unified interface to a set of interfaces in a subsystem. It defines a higher-level interface that makes the subsystem easier to use.
// Subsystem 1: CPU class with methods to freeze, jump to a position, and execute instructions
class CPU {
// Freezes the CPU
freeze() {
console.log("Freezing CPU...");
}
// Jumps to a specific position in memory
jump(position) {
console.log(`Jumping to position ${position}...`);
}
// Executes instructions
execute() {
console.log("Executing instructions...");
}
}
// Subsystem 2: Memory class with a method to load data at a specific position
class Memory {
// Loads data into a specific position in memory
load(position, data) {
console.log(`Loading data '${data}' at position ${position}...`);
}
}
// Subsystem 3: HardDrive class with a method to read data from a specific LBA (Logical Block Addressing)
class HardDrive {
// Reads data from a specific LBA and returns the data
read(lba, size) {
console.log(`Reading ${size} bytes from LBA ${lba}...`);
return "data";
}
}
// Facade: ComputerFacade class to simplify the interaction with the subsystems
class ComputerFacade {
constructor() {
// Initialize subsystems
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
// Method to start the computer by coordinating the subsystems
start() {
// Freeze the CPU
this.cpu.freeze();
// Load data from the hard drive into memory
this.memory.load(0, this.hardDrive.read(0, 1024));
// Jump to the start position in memory
this.cpu.jump(0);
// Execute instructions
this.cpu.execute();
}
}
// Client code: Creating an instance of ComputerFacade and starting the computer
const computer = new ComputerFacade();
computer.start();
Subsystem Classes:
CPU
: Represents the CPU with methods to freeze, jump to a memory position, and execute instructions.Memory
: Represents the memory with a method to load data at a specific position.HardDrive
: Represents the hard drive with a method to read data from a specific Logical Block Addressing (LBA).
Facade Class:
- ComputerFacade: Simplifies the interaction with the subsystems by providing a higher-level interface. It initializes instances of the
CPU
,Memory
, andHardDrive
classes and provides astart
method to coordinate their actions.
Client Code:
- Creates an instance of
ComputerFacade
and calls thestart
method to start the computer, which internally coordinates the actions of theCPU
,Memory
, andHardDrive
subsystems.
Factory Method lets a class defer instantiation to subclasses. It defines an interface for creating a single object, but lets subclasses decide which class to instantiate.
// Class for Car
class Car {
constructor({ doors = 4, state = "brand new", color = "silver" } = {}) {
this.doors = doors;
this.state = state;
this.color = color;
}
}
The Car
class initializes a car object with properties doors
, state
, and color
. Default values are provided using destructuring and default parameters.
// Class for Truck
class Truck {
constructor({ doors = 2, state = "used", color = "blue" } = {}) {
this.doors = doors;
this.state = state;
this.color = color;
}
}
Similar to the Car
class, the Truck
class initializes a truck object with properties doors
, state
, and color
. Default values are also provided using destructuring and default parameters.
// Factory class
class VehicleFactory {
// Method to create a vehicle based on the provided options
createVehicle(options) {
switch (options.vehicleType) {
// If vehicleType is "car", create a new Car
case "car":
return new Car(options);
// If vehicleType is "truck", create a new Truck
case "truck":
return new Truck(options);
// If vehicleType is not recognized, return null
default:
return null;
}
}
}
- The
createVehicle
method is added to theVehicleFactory
class. - It takes an options object as an argument and uses a switch statement to determine which type of vehicle to create based on the
vehicleType
property. - If
vehicleType
is "car", it creates and returns a newCar
object. - If
vehicleType
is "truck", it creates and returns a newTruck
object. - If
vehicleType
is not recognized, it returnsnull
.
const factory = new VehicleFactory();
// Create a car with specified options
const car = factory.createVehicle({
vehicleType: "car",
doors: 4,
color: "red",
state: "new",
});
// Create a truck with specified options
const truck = factory.createVehicle({
vehicleType: "truck",
doors: 2,
color: "black",
state: "used",
});
- An instance of
VehicleFactory
is created. - The
createVehicle
method is called with specific options to create aCar
and aTruck
. - The created car and truck objects have the specified properties.
The Prototype pattern is used to create objects based on a template of an existing object through cloning. It's useful for defining methods and properties that should be shared across all instances of a particular type, promoting efficient memory usage and consistent behavior.
// Define a constructor function
function Person(name, age) {
this.name = name;
this.age = age;
}
// Add a method to the prototype
Person.prototype.greet = function () {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
// Create instances
const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);
// Call the method
person1.greet(); // Output: Hello, my name is Alice and I am 30 years old.
person2.greet(); // Output: Hello, my name is Bob and I am 25 years old.
- Constructor Function:
Person
is a constructor function used to create new objects withname
andage
properties. - Prototype Method: The
greet
method is added toPerson.prototype
, so all instances ofPerson
can use this method without duplicating it for each instance. - Instance Creation:
person1
andperson2
are instances ofPerson
, created using thenew
keyword. - Method Invocation: The
greet
method is called on each instance, demonstrating how each instance can access shared methods.
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. This is useful when exactly one object is needed to coordinate actions across the system.
class Singleton {
// Constructor function for the Singleton class
constructor() {
// Check if an instance already exists
if (!Singleton.instance) {
// If no instance exists, assign the current instance to Singleton.instance
Singleton.instance = this;
}
// Return the single instance
return Singleton.instance;
}
// Example method for the Singleton class
someMethod() {
console.log("Singleton method called");
}
}
Class Declaration: The Singleton
class is declared.
Constructor: The constructor checks if an instance of the class already exists.
if (!Singleton.instance)
: This condition checks ifSingleton.instance
isundefined
ornull
.Singleton.instance = this
: If no instance exists, the current instance (this
) is assigned toSingleton.instance
.return Singleton.instance
: The constructor returns the single instance of the class.
Method: The someMethod
is a simple method that logs a message to the console.
// Create the first instance of the Singleton class
const instance1 = new Singleton();
// Attempt to create a second instance of the Singleton class
const instance2 = new Singleton();
// Check if both instances are the same
console.log(instance1 === instance2); // true
// Call the method on the first instance
instance1.someMethod(); // Singleton method called
Instance Creation:
const instance1 = new Singleton()
: Creates the first instance of theSingleton
class.const instance2 = new Singleton()
: Attempts to create a second instance, but due to theSingleton
pattern, it returns the same instance asinstance1
.
Instance Comparison:
console.log(instance1 === instance2)
: This logs true because bothinstance1
andinstance2
refer to the same instance.
Method Call:
instance1.someMethod()
: Calls thesomeMethod
on the singleton instance, logging "Singleton method called" to the console.
- The Singleton pattern ensures that only one instance of the
Singleton
class is created. - Any subsequent attempts to create a new instance will return the already existing instance.
- This pattern is useful for managing shared resources or coordinating actions across a system.