90. Фабричные функции
Фабрика — функция или метод, создающий объекты без указания конкретного класса. Вместо new SomeClass() вы вызываете createSomething() и получаете готовый объект. Фабрики дают гибкость: один интерфейс — разные реализации.
Фабричная функция vs Конструктор
Заголовок раздела «Фабричная функция vs Конструктор»// Конструктор — нужен new, this, prototypefunction UserConstructor(name, role) { this.name = name this.role = role this.greet = function() { return `Привет, я ${this.name}!` }}const u1 = new UserConstructor('Алексей', 'admin')
// Фабричная функция — просто возвращает объектfunction createUser(name, role) { return { name, role, greet() { return `Привет, я ${name}!` }, isAdmin() { return role === 'admin' }, }}const u2 = createUser('Алексей', 'admin')
// Оба подхода работают, но фабрика:// ✅ Не нужен new// ✅ Лёгкий возврат разных типов объектов// ✅ Нет проблем с this// ✅ Настоящая приватность через замыканиеФабрика с приватным состоянием
Заголовок раздела «Фабрика с приватным состоянием»function createCounter(initial = 0, { min = -Infinity, max = Infinity } = {}) { let count = initial
const clamp = (val) => Math.min(Math.max(val, min), max)
return { increment(by = 1) { count = clamp(count + by); return this }, decrement(by = 1) { count = clamp(count - by); return this }, reset() { count = initial; return this }, get value() { return count }, toString() { return `Counter(${count})` }, }}
const c = createCounter(0, { min: 0, max: 10 })c.increment(5).increment(3).increment(5) // зажато на 10console.log(c.value) // 10
const unlimited = createCounter(100)unlimited.decrement(150)console.log(unlimited.value) // -50Abstract Factory — семейство объектов
Заголовок раздела «Abstract Factory — семейство объектов»Абстрактная фабрика создаёт семейства связанных объектов:
// Абстрактная фабрика UI-компонентовfunction createTheme(variant) { const themes = { dark: { button: () => ({ render: (text) => `<button style="background:#1a1a2e;color:#e0e0ff">${text}</button>` }), input: () => ({ render: (placeholder) => `<input style="background:#16213e;color:#e0e0ff" placeholder="${placeholder}">` }), modal: () => ({ render: (content) => `<div style="background:#0f3460;color:white">${content}</div>` }), }, light: { button: () => ({ render: (text) => `<button style="background:#4361ee;color:white">${text}</button>` }), input: () => ({ render: (placeholder) => `<input style="background:#white;border:1px solid #4361ee" placeholder="${placeholder}">` }), modal: () => ({ render: (content) => `<div style="background:white;border:1px solid #4361ee">${content}</div>` }), } }
const theme = themes[variant] if (!theme) throw new Error(`Неизвестная тема: ${variant}`)
return { createButton: theme.button, createInput: theme.input, createModal: theme.modal, }}
// Используем фабрику — не знаем про конкретные реализацииfunction renderForm(factory) { const button = factory.createButton() const input = factory.createInput() return input.render('Введите имя') + button.render('Отправить')}
console.log(renderForm(createTheme('dark')))console.log(renderForm(createTheme('light')))Factory Method в классах
Заголовок раздела «Factory Method в классах»class Transport { constructor(speed, fuel) { this.speed = speed this.fuel = fuel }
describe() { return `${this.constructor.name}: ${this.speed}км/ч, топливо: ${this.fuel}` }
// Фабричный метод static create(type) { const types = { car: () => new Car(120, 'бензин'), bike: () => new Bike(30, 'мышечная сила'), plane: () => new Plane(900, 'керосин'), } const factory = types[type] if (!factory) throw new Error(`Неизвестный тип: ${type}`) return factory() }}
class Car extends Transport { honk() { return 'Бип!' }}
class Bike extends Transport { ring() { return 'Дзынь!' }}
class Plane extends Transport { takeoff() { return 'Взлетаем!' }}
const car = Transport.create('car')const bike = Transport.create('bike')const plane = Transport.create('plane')
console.log(car.describe()) // Car: 120км/ч, топливо: бензинconsole.log(bike.describe()) // Bike: 30км/ч, топливо: мышечная силаconsole.log(plane.takeoff()) // Взлетаем!Registry Pattern — реестр фабрик
Заголовок раздела «Registry Pattern — реестр фабрик»class PluginRegistry { #factories = new Map()
register(name, factory) { this.#factories.set(name, factory) return this }
create(name, ...args) { const factory = this.#factories.get(name) if (!factory) throw new Error(`Плагин "${name}" не найден`) return factory(...args) }
list() { return [...this.#factories.keys()] }}
const registry = new PluginRegistry()
registry .register('json', (data) => ({ parse: (str) => JSON.parse(str), stringify: (obj) => JSON.stringify(obj, null, 2), })) .register('csv', (delimiter = ',') => ({ parse: (str) => str.split('\n').map(row => row.split(delimiter)), stringify: (data) => data.map(row => row.join(delimiter)).join('\n'), }))
const jsonParser = registry.create('json')console.log(jsonParser.parse('{"name":"Яша"}')) // { name: 'Яша' }
const csvParser = registry.create('csv', ';')console.log(csvParser.stringify([['a', 'b'], ['1', '2']])) // a;b\n1;2