创建型模式

创建型设计模式关注对象的创建过程,帮助我们将对象的创建和使用分离,使系统更加灵活和可扩展。本章将介绍JavaScript中常用的创建型设计模式。

设计模式概述

什么是设计模式

设计模式是软件开发中经过验证的解决方案,它们不是代码,而是解决特定问题的模板和思路。

const patternCategories = {
    creational: {
        focus: '对象的创建过程',
        patterns: ['单例模式', '工厂模式', '建造者模式', '原型模式']
    },
    structural: {
        focus: '对象之间的组合关系',
        patterns: ['适配器模式', '装饰器模式', '代理模式', '外观模式']
    },
    behavioral: {
        focus: '对象之间的通信',
        patterns: ['观察者模式', '策略模式', '命令模式', '迭代器模式']
    }
};

设计原则(SOLID)

const solidPrinciples = {
    S: {
        name: '单一职责原则',
        description: '一个类只负责一项职责',
        example: '用户类只管理用户信息,不处理数据库操作'
    },
    O: {
        name: '开放封闭原则',
        description: '对扩展开放,对修改封闭',
        example: '通过继承扩展功能,而不是修改现有代码'
    },
    L: {
        name: '里氏替换原则',
        description: '子类可以替换父类',
        example: '所有子类都能完成父类的行为'
    },
    I: {
        name: '接口隔离原则',
        description: '不应强迫依赖不使用的接口',
        example: '将大接口拆分为多个小接口'
    },
    D: {
        name: '依赖倒置原则',
        description: '依赖抽象而非具体实现',
        example: '通过接口注入依赖'
    }
};

单例模式(Singleton)

基本实现

单例模式确保一个类只有一个实例,并提供全局访问点。

class Singleton {
    constructor() {
        if (Singleton.instance) {
            return Singleton.instance;
        }
        Singleton.instance = this;
        this.data = {};
    }
    
    setData(key, value) {
        this.data[key] = value;
    }
    
    getData(key) {
        return this.data[key];
    }
}

const instance1 = new Singleton();
const instance2 = new Singleton();

console.log(instance1 === instance2);

instance1.setData('name', '东巴文');
console.log(instance2.getData('name'));

闭包实现

const Singleton = (function() {
    let instance;
    
    function createInstance() {
        return {
            data: {},
            setData(key, value) {
                this.data[key] = value;
            },
            getData(key) {
                return this.data[key];
            }
        };
    }
    
    return {
        getInstance() {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    };
})();

const s1 = Singleton.getInstance();
const s2 = Singleton.getInstance();
console.log(s1 === s2);

现代JavaScript实现

class Singleton {
    static #instance;
    
    constructor() {
        if (Singleton.#instance) {
            return Singleton.#instance;
        }
        Singleton.#instance = this;
    }
    
    static getInstance() {
        if (!Singleton.#instance) {
            Singleton.#instance = new Singleton();
        }
        return Singleton.#instance;
    }
}

const singleton = new Proxy(Singleton, {
    construct(target, args) {
        return target.getInstance();
    }
});

const config = {
    _instance: null,
    
    init(options) {
        if (!this._instance) {
            this._instance = {
                settings: { ...options },
                get(key) {
                    return this.settings[key];
                },
                set(key, value) {
                    this.settings[key] = value;
                }
            };
        }
        return this._instance;
    }
};

应用场景

class DatabaseConnection {
    static #instance = null;
    #connection = null;
    
    constructor(config) {
        if (DatabaseConnection.#instance) {
            return DatabaseConnection.#instance;
        }
        
        this.config = config;
        this.connect();
        DatabaseConnection.#instance = this;
    }
    
    connect() {
        console.log(`连接数据库: ${this.config.host}`);
        this.#connection = { status: 'connected' };
    }
    
    query(sql) {
        if (!this.#connection) {
            throw new Error('数据库未连接');
        }
        console.log(`执行查询: ${sql}`);
        return { results: [] };
    }
    
    static getInstance(config) {
        if (!DatabaseConnection.#instance) {
            DatabaseConnection.#instance = new DatabaseConnection(config);
        }
        return DatabaseConnection.#instance;
    }
}

class Logger {
    static #instance;
    #logs = [];
    
    constructor() {
        if (Logger.#instance) {
            return Logger.#instance;
        }
        Logger.#instance = this;
    }
    
    log(level, message) {
        const entry = {
            timestamp: new Date().toISOString(),
            level,
            message
        };
        this.#logs.push(entry);
        console.log(`[${entry.level}] ${entry.message}`);
    }
    
    info(message) { this.log('INFO', message); }
    warn(message) { this.log('WARN', message); }
    error(message) { this.log('ERROR', message); }
    
    getLogs() { return [...this.#logs]; }
}

工厂模式(Factory)

简单工厂

class Button {
    constructor(text) {
        this.text = text;
    }
    
    render() {
        console.log(`渲染按钮: ${this.text}`);
    }
}

class Input {
    constructor(placeholder) {
        this.placeholder = placeholder;
    }
    
    render() {
        console.log(`渲染输入框: ${this.placeholder}`);
    }
}

class Select {
    constructor(options) {
        this.options = options;
    }
    
    render() {
        console.log(`渲染下拉框: ${this.options.join(', ')}`);
    }
}

function createComponent(type, config) {
    switch (type) {
        case 'button':
            return new Button(config.text);
        case 'input':
            return new Input(config.placeholder);
        case 'select':
            return new Select(config.options);
        default:
            throw new Error(`未知组件类型: ${type}`);
    }
}

const button = createComponent('button', { text: '提交' });
const input = createComponent('input', { placeholder: '请输入' });
const select = createComponent('select', { options: ['A', 'B', 'C'] });

工厂方法

class Document {
    constructor(title) {
        this.title = title;
    }
    
    createPage() {
        throw new Error('子类必须实现 createPage 方法');
    }
}

class TextDocument extends Document {
    createPage() {
        return new TextPage(this.title);
    }
}

class SpreadsheetDocument extends Document {
    createPage() {
        return new SpreadsheetPage(this.title);
    }
}

class TextPage {
    constructor(title) {
        this.type = 'text';
        this.title = title;
        this.content = '';
    }
}

class SpreadsheetPage {
    constructor(title) {
        this.type = 'spreadsheet';
        this.title = title;
        this.cells = [];
    }
}

function createDocument(type, title) {
    const documentClasses = {
        text: TextDocument,
        spreadsheet: SpreadsheetDocument
    };
    
    const DocumentClass = documentClasses[type];
    if (!DocumentClass) {
        throw new Error(`未知文档类型: ${type}`);
    }
    
    return new DocumentClass(title);
}

抽象工厂

class UIFactory {
    createButton() {
        throw new Error('必须实现 createButton');
    }
    
    createInput() {
        throw new Error('必须实现 createInput');
    }
}

class LightUIFactory extends UIFactory {
    createButton() {
        return new LightButton();
    }
    
    createInput() {
        return new LightInput();
    }
}

class DarkUIFactory extends UIFactory {
    createButton() {
        return new DarkButton();
    }
    
    createInput() {
        return new DarkInput();
    }
}

class LightButton {
    render() {
        return '<button class="light-btn">浅色按钮</button>';
    }
}

class DarkButton {
    render() {
        return '<button class="dark-btn">深色按钮</button>';
    }
}

class LightInput {
    render() {
        return '<input class="light-input" />';
    }
}

class DarkInput {
    render() {
        return '<input class="dark-input" />';
    }
}

function renderUI(theme) {
    const factory = theme === 'dark' ? new DarkUIFactory() : new LightUIFactory();
    
    const button = factory.createButton();
    const input = factory.createInput();
    
    console.log(button.render());
    console.log(input.render());
}

动态工厂

class DynamicFactory {
    constructor() {
        this.registry = new Map();
    }
    
    register(type, constructor) {
        this.registry.set(type, constructor);
        return this;
    }
    
    create(type, ...args) {
        const Constructor = this.registry.get(type);
        if (!Constructor) {
            throw new Error(`未注册的类型: ${type}`);
        }
        return new Constructor(...args);
    }
    
    has(type) {
        return this.registry.has(type);
    }
    
    remove(type) {
        this.registry.delete(type);
    }
}

const factory = new DynamicFactory();

factory
    .register('user', class {
        constructor(name, email) {
            this.name = name;
            this.email = email;
        }
    })
    .register('product', class {
        constructor(name, price) {
            this.name = name;
            this.price = price;
        }
    });

const user = factory.create('user', '张三', 'zhangsan@example.com');
const product = factory.create('product', '商品A', 99.9);

建造者模式(Builder)

基本实现

建造者模式将复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。

class Pizza {
    constructor() {
        this.size = null;
        this.crust = null;
        this.sauce = null;
        this.toppings = [];
        this.cheese = null;
    }
    
    describe() {
        return `${this.size}${this.crust}${this.sauce}酱 ` +
               `${this.toppings.join('、')} ${this.cheese ? '加芝士' : '不加芝士'}`;
    }
}

class PizzaBuilder {
    constructor() {
        this.pizza = new Pizza();
    }
    
    setSize(size) {
        this.pizza.size = size;
        return this;
    }
    
    setCrust(crust) {
        this.pizza.crust = crust;
        return this;
    }
    
    setSauce(sauce) {
        this.pizza.sauce = sauce;
        return this;
    }
    
    addTopping(topping) {
        this.pizza.toppings.push(topping);
        return this;
    }
    
    addCheese() {
        this.pizza.cheese = true;
        return this;
    }
    
    build() {
        if (!this.pizza.size) throw new Error('必须指定尺寸');
        if (!this.pizza.crust) throw new Error('必须指定饼底');
        return this.pizza;
    }
}

const pizza = new PizzaBuilder()
    .setSize(12)
    .setCrust('薄底')
    .setSauce('番茄')
    .addTopping('蘑菇')
    .addTopping('青椒')
    .addCheese()
    .build();

console.log(pizza.describe());

链式调用

class HttpRequestBuilder {
    constructor() {
        this.config = {
            url: '',
            method: 'GET',
            headers: {},
            params: {},
            body: null,
            timeout: 5000
        };
    }
    
    url(url) {
        this.config.url = url;
        return this;
    }
    
    method(method) {
        this.config.method = method.toUpperCase();
        return this;
    }
    
    header(key, value) {
        this.config.headers[key] = value;
        return this;
    }
    
    params(params) {
        Object.assign(this.config.params, params);
        return this;
    }
    
    body(body) {
        this.config.body = body;
        return this;
    }
    
    timeout(ms) {
        this.config.timeout = ms;
        return this;
    }
    
    async send() {
        const { url, method, headers, params, body, timeout } = this.config;
        
        const queryString = new URLSearchParams(params).toString();
        const fullUrl = queryString ? `${url}?${queryString}` : url;
        
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), timeout);
        
        try {
            const response = await fetch(fullUrl, {
                method,
                headers,
                body: body ? JSON.stringify(body) : null,
                signal: controller.signal
            });
            
            return response.json();
        } finally {
            clearTimeout(timeoutId);
        }
    }
}

const response = await new HttpRequestBuilder()
    .url('/api/users')
    .method('POST')
    .header('Content-Type', 'application/json')
    .header('Authorization', 'Bearer token')
    .body({ name: '张三', email: 'zhangsan@example.com' })
    .timeout(3000)
    .send();

复杂对象构建

class QueryBuilder {
    constructor() {
        this.table = '';
        this.fields = ['*'];
        this.conditions = [];
        this.orderByClause = '';
        this.limitValue = null;
        this.offsetValue = null;
    }
    
    select(...fields) {
        this.fields = fields.length > 0 ? fields : ['*'];
        return this;
    }
    
    from(table) {
        this.table = table;
        return this;
    }
    
    where(condition, ...params) {
        this.conditions.push({ condition, params });
        return this;
    }
    
    whereIn(field, values) {
        const placeholders = values.map(() => '?').join(', ');
        this.conditions.push({
            condition: `${field} IN (${placeholders})`,
            params: values
        });
        return this;
    }
    
    orderBy(field, direction = 'ASC') {
        this.orderByClause = `ORDER BY ${field} ${direction}`;
        return this;
    }
    
    limit(limit) {
        this.limitValue = limit;
        return this;
    }
    
    offset(offset) {
        this.offsetValue = offset;
        return this;
    }
    
    build() {
        if (!this.table) {
            throw new Error('必须指定表名');
        }
        
        let sql = `SELECT ${this.fields.join(', ')} FROM ${this.table}`;
        const params = [];
        
        if (this.conditions.length > 0) {
            const whereClauses = this.conditions.map(c => c.condition);
            sql += ` WHERE ${whereClauses.join(' AND ')}`;
            this.conditions.forEach(c => params.push(...c.params));
        }
        
        if (this.orderByClause) {
            sql += ` ${this.orderByClause}`;
        }
        
        if (this.limitValue !== null) {
            sql += ` LIMIT ?`;
            params.push(this.limitValue);
        }
        
        if (this.offsetValue !== null) {
            sql += ` OFFSET ?`;
            params.push(this.offsetValue);
        }
        
        return { sql, params };
    }
}

const query = new QueryBuilder()
    .select('id', 'name', 'email')
    .from('users')
    .where('status = ?', 'active')
    .whereIn('role', ['admin', 'editor'])
    .orderBy('created_at', 'DESC')
    .limit(10)
    .offset(20)
    .build();

console.log(query.sql);
console.log(query.params);

原型模式(Prototype)

基本实现

原型模式通过复制现有对象来创建新对象,而不是从头创建。

const prototype = {
    name: '默认名称',
    category: '默认分类',
    tags: [],
    
    clone() {
        const cloned = Object.create(this);
        cloned.tags = [...this.tags];
        return cloned;
    }
};

const product1 = prototype.clone();
product1.name = '产品A';
product1.tags.push('热销');

const product2 = prototype.clone();
product2.name = '产品B';

console.log(product1.tags);
console.log(product2.tags);

类实现

class Shape {
    constructor(type, color, position) {
        this.type = type;
        this.color = color;
        this.position = { ...position };
    }
    
    clone() {
        return new Shape(this.type, this.color, this.position);
    }
    
    draw() {
        console.log(`绘制${this.color}色的${this.type}在位置(${this.position.x}, ${this.position.y})`);
    }
}

const originalShape = new Shape('圆形', '红色', { x: 100, y: 100 });
const clonedShape = originalShape.clone();
clonedShape.color = '蓝色';
clonedShape.position.x = 200;

originalShape.draw();
clonedShape.draw();

深拷贝实现

function deepClone(obj, hash = new WeakMap()) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    
    if (hash.has(obj)) {
        return hash.get(obj);
    }
    
    if (obj instanceof Date) {
        return new Date(obj);
    }
    
    if (obj instanceof RegExp) {
        return new RegExp(obj);
    }
    
    if (obj instanceof Map) {
        const cloned = new Map();
        hash.set(obj, cloned);
        obj.forEach((value, key) => {
            cloned.set(deepClone(key, hash), deepClone(value, hash));
        });
        return cloned;
    }
    
    if (obj instanceof Set) {
        const cloned = new Set();
        hash.set(obj, cloned);
        obj.forEach(value => {
            cloned.add(deepClone(value, hash));
        });
        return cloned;
    }
    
    if (Array.isArray(obj)) {
        const cloned = [];
        hash.set(obj, cloned);
        for (let i = 0; i < obj.length; i++) {
            cloned[i] = deepClone(obj[i], hash);
        }
        return cloned;
    }
    
    const cloned = Object.create(Object.getPrototypeOf(obj));
    hash.set(obj, cloned);
    
    for (const key of Object.keys(obj)) {
        cloned[key] = deepClone(obj[key], hash);
    }
    
    for (const symbol of Object.getOwnPropertySymbols(obj)) {
        cloned[symbol] = deepClone(obj[symbol], hash);
    }
    
    return cloned;
}

class PrototypeRegistry {
    constructor() {
        this.prototypes = new Map();
    }
    
    register(key, prototype) {
        this.prototypes.set(key, prototype);
    }
    
    clone(key) {
        const prototype = this.prototypes.get(key);
        if (!prototype) {
            throw new Error(`未找到原型: ${key}`);
        }
        return deepClone(prototype);
    }
}

实际应用

class EmailTemplate {
    constructor(config) {
        this.subject = config.subject;
        this.from = config.from;
        this.to = [];
        this.body = config.body;
        this.variables = {};
    }
    
    setVariable(key, value) {
        this.variables[key] = value;
        return this;
    }
    
    addTo(email) {
        this.to.push(email);
        return this;
    }
    
    render() {
        let body = this.body;
        for (const [key, value] of Object.entries(this.variables)) {
            body = body.replace(new RegExp(`{{${key}}}`, 'g'), value);
        }
        return body;
    }
    
    clone() {
        const cloned = new EmailTemplate({
            subject: this.subject,
            from: this.from,
            body: this.body
        });
        cloned.to = [...this.to];
        cloned.variables = { ...this.variables };
        return cloned;
    }
}

const welcomeTemplate = new EmailTemplate({
    subject: '欢迎加入',
    from: 'noreply@example.com',
    body: '亲爱的{{name}},欢迎加入我们的社区!'
});

const email1 = welcomeTemplate.clone()
    .addTo('user1@example.com')
    .setVariable('name', '张三');

const email2 = welcomeTemplate.clone()
    .addTo('user2@example.com')
    .setVariable('name', '李四');

console.log(email1.render());
console.log(email2.render());

对象池模式

class ObjectPool {
    constructor(factory, options = {}) {
        this.factory = factory;
        this.maxSize = options.maxSize || 100;
        this.initialSize = options.initialSize || 10;
        this.pool = [];
        this.active = new Set();
        
        this.initialize();
    }
    
    initialize() {
        for (let i = 0; i < this.initialSize; i++) {
            this.pool.push(this.factory());
        }
    }
    
    acquire(...args) {
        let obj;
        
        if (this.pool.length > 0) {
            obj = this.pool.pop();
        } else if (this.active.size < this.maxSize) {
            obj = this.factory();
        } else {
            throw new Error('对象池已满');
        }
        
        if (obj.init) {
            obj.init(...args);
        }
        
        this.active.add(obj);
        return obj;
    }
    
    release(obj) {
        if (!this.active.has(obj)) return;
        
        this.active.delete(obj);
        
        if (obj.reset) {
            obj.reset();
        }
        
        this.pool.push(obj);
    }
    
    get size() {
        return this.pool.length + this.active.size;
    }
    
    get available() {
        return this.pool.length;
    }
    
    clear() {
        this.pool = [];
        this.active.clear();
    }
}

class Bullet {
    constructor() {
        this.reset();
    }
    
    init(x, y, velocity) {
        this.x = x;
        this.y = y;
        this.velocity = { ...velocity };
        this.active = true;
    }
    
    reset() {
        this.x = 0;
        this.y = 0;
        this.velocity = { x: 0, y: 0 };
        this.active = false;
    }
    
    update() {
        this.x += this.velocity.x;
        this.y += this.velocity.y;
    }
}

const bulletPool = new ObjectPool(() => new Bullet(), {
    initialSize: 50,
    maxSize: 200
});

东巴文小贴士

🏭 工厂模式选择指南

  • 简单工厂:创建逻辑简单,类型较少
  • 工厂方法:需要扩展新的产品类型
  • 抽象工厂:需要创建产品族(相关产品组合)

📦 单例模式注意事项

单例模式虽然方便,但也带来一些问题:

  • 全局状态可能导致代码难以测试
  • 隐藏的依赖关系
  • 多线程环境下的竞态条件(JavaScript中较少见)

考虑使用依赖注入替代单例

下一步

下一章将探讨 结构型模式,学习如何组合类和对象以形成更大的结构。