Creational Patterns
Singleton
javascript
// Ensure only one instance exists
class Database {
constructor() {
if (Database.instance) {
return Database.instance;
}
this.connection = null;
Database.instance = this;
}
connect() { /* ... */ }
}
const db1 = new Database();
const db2 = new Database();
console.log(db1 === db2); // trueFactory
javascript
// Create objects without specifying exact class
class CarFactory {
create(type) {
switch (type) {
case 'sedan':
return new Sedan();
case 'suv':
return new SUV();
default:
throw new Error('Unknown car type');
}
}
}
const factory = new CarFactory();
const sedan = factory.create('sedan');Structural Patterns
Adapter
javascript
// Make incompatible interfaces work together
class OldAPI {
request() { return 'old format'; }
}
class NewAPI {
fetch() { return 'new format'; }
}
class Adapter {
constructor(newAPI) {
this.newAPI = newAPI;
}
request() {
const data = this.newAPI.fetch();
return `converted: ${data}`;
}
}Decorator
javascript
// Add behavior without modifying class
function withLogging(fn) {
return function(...args) {
console.log(`Calling ${fn.name} with`, args);
const result = fn.apply(this, args);
console.log(`Result:`, result);
return result;
};
}
const add = (a, b) => a + b;
const loggedAdd = withLogging(add);
loggedAdd(2, 3); // Logs: Calling add with [2, 3], Result: 5Behavioral Patterns
Observer
javascript
// Subscribe to notifications
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
const subject = new Subject();
subject.subscribe({
update: (data) => console.log('Received:', data)
});
subject.notify('Hello!');Strategy
javascript
// Swap algorithms at runtime
class PaymentStrategy {
pay(amount) { throw new Error('Must implement'); }
}
class CreditCardPayment extends PaymentStrategy {
pay(amount) {
console.log(`Paid $${amount} with credit card`);
}
}
class PayPalPayment extends PaymentStrategy {
pay(amount) {
console.log(`Paid $${amount} with PayPal`);
}
}
class Cart {
constructor(paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
checkout(amount) {
this.paymentStrategy.pay(amount);
}
}Modern Patterns
Module Pattern
javascript
// Encapsulation with closures
const Module = (() => {
let privateVar = 0;
return {
increment() {
privateVar++;
},
getCount() {
return privateVar;
}
};
})();
Module.increment();
console.log(Module.getCount()); // 1
console.log(Module.privateVar); // undefinedPub/Sub
javascript
// Event-driven communication
class PubSub {
constructor() {
this.events = {};
}
subscribe(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
publish(event, data) {
if (this.events[event]) {
this.events[event].forEach(cb => cb(data));
}
}
}When to Use
Patterns solve common problems but aren't silver bullets:
- 1Overuse: Don't force patterns where simple code works
- 2Context: Consider team familiarity and project needs
- 3Simplicity: Favor simple solutions over clever patterns
- 4Pragmatism: Use patterns that actually solve your problem
Learn patterns, then know when to break them.