Singleton Pattern
Singleton Pattern
Basic Singleton
class Database {
private static instance: Database;
private connected: boolean = false;
private constructor(private config: DbConfig) {}
static getInstance(config?: DbConfig): Database {
if (!Database.instance) {
if (!config) throw new Error('Config required for first initialization');
Database.instance = new Database(config);
}
return Database.instance;
}
async connect(): Promise<void> {
if (this.connected) return;
console.log('Connecting to database...');
this.connected = true;
}
query<T>(sql: string): T[] {
if (!this.connected) throw new Error('Not connected');
return [];
}
}
// Usage
const db = Database.getInstance({ host: 'localhost', port: 5432 });
await db.connect();
const users = db.query<User>('SELECT * FROM users');
Module-Level Singleton
// logger.ts - simpler approach
interface Logger {
info(msg: string): void;
error(msg: string, err?: Error): void;
}
class ConsoleLogger implements Logger {
info(msg: string): void {
console.log(`[INFO] ${msg}`);
}
error(msg: string, err?: Error): void {
console.error(`[ERROR] ${msg}`, err);
}
}
// Module exports singleton instance
export const logger = new ConsoleLogger();
Factory Pattern
Factory Pattern
Simple Factory
interface Notification {
send(message: string): void;
}
class EmailNotification implements Notification {
send(message: string): void {
console.log(`Email: ${message}`);
}
}
class SMSNotification implements Notification {
send(message: string): void {
console.log(`SMS: ${message}`);
}
}
class PushNotification implements Notification {
send(message: string): void {
console.log(`Push: ${message}`);
}
}
class NotificationFactory {
static create(type: 'email' | 'sms' | 'push'): Notification {
switch (type) {
case 'email': return new EmailNotification();
case 'sms': return new SMSNotification();
case 'push': return new PushNotification();
}
}
}
const notifier = NotificationFactory.create('email');
notifier.send('Hello!');
Abstract Factory
interface Button {
render(): string;
onClick(handler: () => void): void;
}
interface Input {
render(): string;
getValue(): string;
}
interface UIFactory {
createButton(): Button;
createInput(): Input;
}
class MaterialButton implements Button {
render(): string { return '<button class="mdc-button"></button>'; }
onClick(handler: () => void): void { /* ... */ }
}
class MaterialInput implements Input {
render(): string { return '<input class="mdc-textfield" />'; }
getValue(): string { return ''; }
}
class MaterialFactory implements UIFactory {
createButton(): Button { return new MaterialButton(); }
createInput(): Input { return new MaterialInput(); }
}
Factory Method in Classes
abstract class PaymentProcessor {
abstract createGateway(): PaymentGateway;
processPayment(amount: number): boolean {
const gateway = this.createGateway();
return gateway.charge(amount);
}
}
class StripeProcessor extends PaymentProcessor {
createGateway(): PaymentGateway {
return new StripeGateway();
}
}
class PayPalProcessor extends PaymentProcessor {
createGateway(): PaymentGateway {
return new PayPalGateway();
}
}
Builder Pattern
Builder Pattern
Fluent Builder
class QueryBuilder {
private table: string = '';
private conditions: string[] = [];
private orderBy: string = '';
private limitCount: number = 0;
from(table: string): this {
this.table = table;
return this;
}
where(condition: string): this {
this.conditions.push(condition);
return this;
}
order(column: string): this {
this.orderBy = column;
return this;
}
limit(n: number): this {
this.limitCount = n;
return this;
}
build(): string {
let query = `SELECT * FROM ${this.table}`;
if (this.conditions.length) {
query += ` WHERE ${this.conditions.join(' AND ')}`;
}
if (this.orderBy) {
query += ` ORDER BY ${this.orderBy}`;
}
if (this.limitCount) {
query += ` LIMIT ${this.limitCount}`;
}
return query;
}
}
// Usage
const query = new QueryBuilder()
.from('users')
.where('age > 18')
.where('active = true')
.order('name')
.limit(10)
.build();
Type-Safe Builder
class ConfigBuilder {
private config: Record<string, unknown> = {};
set<K extends string, V>(key: K, value: V): ConfigBuilder {
this.config[key] = value;
return this;
}
build<T>(): T {
return this.config as T;
}
}
const config = new ConfigBuilder()
.set('host', 'localhost')
.set('port', 3000)
.set('debug', true)
.build<{ host: string; port: number; boolean: boolean }>();
Prototype Pattern
Prototype Pattern
Object Cloning
interface Prototype<T> {
clone(): T;
}
class UserConfig implements Prototype<UserConfig> {
constructor(
public theme: string,
public language: string,
public notifications: boolean,
public permissions: string[]
) {}
clone(): UserConfig {
// Deep clone permissions array
return new UserConfig(
this.theme,
this.language,
this.notifications,
[...this.permissions]
);
}
}
// Usage
const defaultConfig = new UserConfig('dark', 'en', true, ['read', 'write']);
const adminConfig = defaultConfig.clone();
adminConfig.permissions.push('delete');
// original.permissions unchanged
Spread Operator Cloning
interface UserProfile {
name: string;
settings: {
theme: string;
notifications: boolean;
};
}
function deepClone<T>(obj: T): T {
return structuredClone(obj);
}
const original: UserProfile = {
name: 'Alice',
settings: { theme: 'dark', notifications: true }
};
const clone = deepClone(original);
clone.settings.theme = 'light';
// original.settings.theme still 'dark'