您现在的位置是:网站首页 > 工厂模式(Factory Pattern)的实现与应用文章详情
工厂模式(Factory Pattern)的实现与应用
陈川
【
JavaScript
】
44373人已围观
12490字
工厂模式是一种创建型设计模式,它提供了一种封装对象创建过程的方式,使得客户端代码不需要直接依赖具体类。这种模式通过定义一个创建对象的接口,让子类决定实例化哪一个类,从而将对象的创建与使用分离。
工厂模式的基本概念
工厂模式的核心思想是将对象的创建过程抽象出来,形成一个独立的"工厂"角色。根据抽象程度的不同,工厂模式可以分为三种形式:
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
在JavaScript中,由于语言的动态特性,工厂模式的实现相比静态类型语言更加灵活。我们可以利用函数、对象或类来实现不同形式的工厂模式。
简单工厂模式实现
简单工厂模式是最基础的工厂实现,它通过一个工厂类(或函数)来创建不同类型的对象,而不需要暴露具体的创建逻辑。
class Car {
constructor(options) {
this.type = options.type || '普通汽车';
this.color = options.color || '白色';
}
drive() {
console.log(`${this.color}的${this.type}正在行驶`);
}
}
class CarFactory {
static create(type, options) {
switch(type) {
case 'sports':
return new Car({ ...options, type: '跑车' });
case 'suv':
return new Car({ ...options, type: 'SUV' });
default:
return new Car(options);
}
}
}
// 使用工厂创建汽车
const sportsCar = CarFactory.create('sports', { color: '红色' });
const suv = CarFactory.create('suv', { color: '黑色' });
sportsCar.drive(); // 红色的跑车正在行驶
suv.drive(); // 黑色的SUV正在行驶
这种模式的优点是客户端不需要知道具体产品的类名,只需要知道工厂类和参数即可。缺点是当需要添加新产品时,必须修改工厂类的逻辑,违反了开闭原则。
工厂方法模式实现
工厂方法模式是对简单工厂模式的进一步抽象,它定义了一个创建对象的接口,但让子类决定实例化哪个类。工厂方法让类的实例化推迟到子类。
// 抽象工厂类
class VehicleFactory {
createVehicle() {
throw new Error('此方法必须由子类实现');
}
}
// 具体工厂类 - 汽车工厂
class CarFactory extends VehicleFactory {
createVehicle(options) {
return new Car(options);
}
}
// 具体工厂类 - 摩托车工厂
class MotorcycleFactory extends VehicleFactory {
createVehicle(options) {
return new Motorcycle(options);
}
}
// 具体产品类 - 汽车
class Car {
constructor(options) {
this.type = '汽车';
this.color = options.color;
}
drive() {
console.log(`${this.color}的${this.type}正在行驶`);
}
}
// 具体产品类 - 摩托车
class Motorcycle {
constructor(options) {
this.type = '摩托车';
this.color = options.color;
}
ride() {
console.log(`${this.color}的${this.type}正在骑行`);
}
}
// 使用工厂
const carFactory = new CarFactory();
const myCar = carFactory.createVehicle({ color: '蓝色' });
myCar.drive();
const motorcycleFactory = new MotorcycleFactory();
const myMotorcycle = motorcycleFactory.createVehicle({ color: '黄色' });
myMotorcycle.ride();
工厂方法模式符合开闭原则,当需要添加新产品时,只需要添加新的工厂类即可,不需要修改现有代码。但是每增加一个产品类,就需要增加一个对应的工厂类,增加了系统的复杂度。
抽象工厂模式实现
抽象工厂模式是工厂方法模式的升级版,它用于创建一系列相关或相互依赖的对象,而不需要指定它们具体的类。
// 抽象工厂接口
class UIFactory {
createButton() {}
createCheckbox() {}
}
// 具体工厂 - Material风格UI组件
class MaterialUIFactory extends UIFactory {
createButton() {
return new MaterialButton();
}
createCheckbox() {
return new MaterialCheckbox();
}
}
// 具体工厂 - Flat风格UI组件
class FlatUIFactory extends UIFactory {
createButton() {
return new FlatButton();
}
createCheckbox() {
return new FlatCheckbox();
}
}
// 具体产品 - Material按钮
class MaterialButton {
render() {
console.log('渲染Material风格的按钮');
}
}
// 具体产品 - Material复选框
class MaterialCheckbox {
render() {
console.log('渲染Material风格的复选框');
}
}
// 具体产品 - Flat按钮
class FlatButton {
render() {
console.log('渲染Flat风格的按钮');
}
}
// 具体产品 - Flat复选框
class FlatCheckbox {
render() {
console.log('渲染Flat风格的复选框');
}
}
// 客户端代码
function createUI(factory) {
const button = factory.createButton();
const checkbox = factory.createCheckbox();
return { button, checkbox };
}
// 使用Material风格
const materialUI = createUI(new MaterialUIFactory());
materialUI.button.render();
materialUI.checkbox.render();
// 使用Flat风格
const flatUI = createUI(new FlatUIFactory());
flatUI.button.render();
flatUI.checkbox.render();
抽象工厂模式特别适合需要创建一系列相关产品的场景,它能确保客户端始终使用同一系列的产品。当需要增加新的产品系列时,只需要增加新的具体工厂类即可,符合开闭原则。
JavaScript中的工厂函数实现
在JavaScript中,我们还可以使用工厂函数来实现工厂模式,这种方式更加简洁灵活:
// 工厂函数实现
function createUser(type, options) {
switch(type) {
case 'admin':
return {
...options,
role: 'admin',
permissions: ['create', 'read', 'update', 'delete'],
greet() {
console.log(`您好,管理员${this.name}`);
}
};
case 'customer':
return {
...options,
role: 'customer',
permissions: ['read'],
greet() {
console.log(`欢迎您,顾客${this.name}`);
}
};
default:
throw new Error('未知的用户类型');
}
}
// 使用工厂函数
const admin = createUser('admin', { name: '张三' });
const customer = createUser('customer', { name: '李四' });
admin.greet(); // 您好,管理员张三
customer.greet(); // 欢迎您,顾客李四
工厂函数在JavaScript中非常常见,它利用了JavaScript的动态特性,可以灵活地创建和返回不同类型的对象。
工厂模式在前端框架中的应用
工厂模式在前端框架中有广泛的应用,例如React中的createElement就是一个典型的工厂方法:
// React.createElement的简化实现
function createElement(type, props, ...children) {
return {
type,
props: {
...props,
children: children.length <= 1 ? children[0] : children
}
};
}
// 使用工厂方法创建React元素
const element = createElement('div', { className: 'container' },
createElement('h1', null, 'Hello World'),
createElement('p', null, 'This is a paragraph')
);
Vue中的组件系统也使用了工厂模式的思想,每个组件都可以看作是一个工厂,负责创建组件实例:
// Vue组件工厂的简化示例
function createComponent(options) {
return {
...options,
data() {
return options.data ? options.data() : {};
},
mounted() {
options.mounted && options.mounted.call(this);
}
// 其他生命周期和方法...
};
}
// 使用组件工厂
const MyComponent = createComponent({
data() {
return { count: 0 };
},
methods: {
increment() {
this.count++;
}
},
mounted() {
console.log('组件已挂载');
}
});
工厂模式与依赖注入
工厂模式经常与依赖注入(DI)一起使用,用于管理对象的创建和依赖关系:
// 依赖注入容器
class Container {
constructor() {
this.services = {};
this.factories = {};
}
register(name, factory) {
this.factories[name] = factory;
}
get(name) {
if (!this.services[name]) {
this.services[name] = this.factories[name](this);
}
return this.services[name];
}
}
// 使用依赖注入容器
const container = new Container();
// 注册服务工厂
container.register('logger', () => {
return {
log: (message) => console.log(`[LOG]: ${message}`)
};
});
container.register('userService', (c) => {
const logger = c.get('logger');
return {
getUsers: () => {
logger.log('获取用户列表');
return ['张三', '李四'];
}
};
});
// 使用服务
const userService = container.get('userService');
console.log(userService.getUsers()); // [LOG]: 获取用户列表 \n ['张三', '李四']
这种模式在现代前端框架如Angular中非常常见,它使得代码更加模块化,便于测试和维护。
工厂模式的优缺点
优点
- 封装创建逻辑:将对象的创建过程集中管理,客户端代码不需要关心具体的创建细节
- 解耦:降低了客户端代码与具体产品类之间的耦合度
- 扩展性好:当需要添加新产品时,只需要扩展工厂类,不需要修改现有代码(工厂方法模式和抽象工厂模式)
- 统一管理:可以对对象的创建进行统一的管理和控制,如实现单例、缓存等
缺点
- 增加复杂度:每增加一个产品,就需要增加一个具体工厂类(工厂方法模式),增加了系统的复杂度
- 抽象性高:抽象工厂模式引入了许多接口和类,增加了系统的抽象性和理解难度
- 不适用于简单对象:对于简单的对象创建,使用工厂模式可能会显得过于复杂
工厂模式与其他设计模式的关系
工厂模式经常与其他设计模式一起使用或相互补充:
- 与单例模式:工厂可以返回单例对象,确保系统中某个类只有一个实例
- 与原型模式:工厂可以使用原型模式来克隆对象,而不是每次都创建新实例
- 与装饰器模式:工厂可以在返回对象前对其进行装饰,添加额外的功能
- 与策略模式:工厂可以根据不同的策略创建不同的对象
实际应用场景示例
场景一:跨平台UI组件
假设我们需要开发一个跨平台的UI库,支持Web和移动端:
// 抽象工厂
class UIComponentsFactory {
createButton() {}
createDialog() {}
}
// Web组件工厂
class WebComponentsFactory extends UIComponentsFactory {
createButton() {
return new WebButton();
}
createDialog() {
return new WebDialog();
}
}
// 移动端组件工厂
class MobileComponentsFactory extends UIComponentsFactory {
createButton() {
return new MobileButton();
}
createDialog() {
return new MobileDialog();
}
}
// 具体产品
class WebButton {
render() {
console.log('渲染Web风格的按钮');
}
}
class WebDialog {
render() {
console.log('渲染Web风格的对话框');
}
}
class MobileButton {
render() {
console.log('渲染移动端风格的按钮');
}
}
class MobileDialog {
render() {
console.log('渲染移动端风格的对话框');
}
}
// 客户端代码
function renderUI(factory) {
const button = factory.createButton();
const dialog = factory.createDialog();
button.render();
dialog.render();
}
// 根据平台选择工厂
const platform = 'web'; // 或'mobile'
const factory = platform === 'web'
? new WebComponentsFactory()
: new MobileComponentsFactory();
renderUI(factory);
场景二:电商系统中的折扣策略
电商系统中不同的用户类型有不同的折扣策略:
// 折扣策略工厂
class DiscountStrategyFactory {
static create(userType) {
switch(userType) {
case 'vip':
return new VipDiscountStrategy();
case 'regular':
return new RegularDiscountStrategy();
case 'new':
return new NewCustomerDiscountStrategy();
default:
return new NoDiscountStrategy();
}
}
}
// 具体策略
class VipDiscountStrategy {
calculate(price) {
return price * 0.8; // 8折
}
}
class RegularDiscountStrategy {
calculate(price) {
return price * 0.9; // 9折
}
}
class NewCustomerDiscountStrategy {
calculate(price) {
return price * 0.95; // 95折
}
}
class NoDiscountStrategy {
calculate(price) {
return price; // 无折扣
}
}
// 使用工厂
const vipDiscount = DiscountStrategyFactory.create('vip');
console.log(vipDiscount.calculate(100)); // 80
const regularDiscount = DiscountStrategyFactory.create('regular');
console.log(regularDiscount.calculate(100)); // 90
场景三:游戏中的角色创建
游戏开发中,工厂模式常用于创建不同类型的游戏角色:
// 角色工厂
class CharacterFactory {
static create(type, name) {
switch(type) {
case 'warrior':
return new Warrior(name);
case 'mage':
return new Mage(name);
case 'archer':
return new Archer(name);
default:
throw new Error('未知的角色类型');
}
}
}
// 具体角色类
class Warrior {
constructor(name) {
this.name = name;
this.health = 150;
this.attack = 20;
}
attackEnemy() {
console.log(`${this.name}用剑攻击,造成${this.attack}点伤害`);
}
}
class Mage {
constructor(name) {
this.name = name;
this.health = 80;
this.attack = 30;
}
attackEnemy() {
console.log(`${this.name}施放火球术,造成${this.attack}点伤害`);
}
}
class Archer {
constructor(name) {
this.name = name;
this.health = 100;
this.attack = 25;
}
attackEnemy() {
console.log(`${this.name}射箭,造成${this.attack}点伤害`);
}
}
// 创建游戏角色
const hero1 = CharacterFactory.create('warrior', '亚瑟');
const hero2 = CharacterFactory.create('mage', '梅林');
hero1.attackEnemy(); // 亚瑟用剑攻击,造成20点伤害
hero2.attackEnemy(); // 梅林施放火球术,造成30点伤害
工厂模式的变体与扩展
延迟初始化工厂
工厂可以延迟对象的创建,直到真正需要时才创建实例:
class LazyFactory {
constructor(creatorFn) {
this.creatorFn = creatorFn;
this.instance = null;
}
getInstance() {
if (!this.instance) {
this.instance = this.creatorFn();
}
return this.instance;
}
}
// 使用延迟工厂
const heavyObjectFactory = new LazyFactory(() => {
console.log('创建重量级对象...');
return { data: '大量数据' };
});
// 第一次访问时才会创建对象
console.log(heavyObjectFactory.getInstance().data); // 创建重量级对象... \n 大量数据
// 第二次访问直接返回已有实例
console.log(heavyObjectFactory.getInstance().data); // 大量数据
多例工厂
工厂可以控制实例的数量,实现多例模式:
class MultiInstanceFactory {
constructor(creatorFn, maxInstances = 3) {
this.creatorFn = creatorFn;
this.maxInstances = maxInstances;
this.instances = [];
}
getInstance() {
if (this.instances.length < this.maxInstances) {
const newInstance = this.creatorFn();
this.instances.push(newInstance);
return newInstance;
}
// 循环使用已有实例
const instance = this.instances.shift();
this.instances.push(instance);
return instance;
}
}
// 使用多例工厂
const connectionFactory = new MultiInstanceFactory(() => {
console.log('创建新的数据库连接');
return { id: Math.random().toString(36).substr(2, 9) };
}, 2);
// 获取连接
const conn1 = connectionFactory.getInstance(); // 创建新的数据库连接
const conn2 = connectionFactory.getInstance(); // 创建新的数据库连接
const conn3 = connectionFactory.getInstance(); // 返回第一个连接
console.log(conn1 === conn3); // true
带缓存的工厂
工厂可以实现缓存机制,避免重复创建相同的对象:
class CachedFactory {
constructor(creatorFn) {
this.creatorFn = creatorFn;
this.cache = new Map();
}
create(key, ...args) {
if (this.cache.has(key)) {
return this.cache.get(key);
}
const instance = this.creatorFn(...args);
this.cache.set(key, instance);
return instance;
}
}
// 使用带缓存的工厂
const userFactory = new CachedFactory((id, name) => {
console.log(`创建用户${name}`);
return { id, name };
});
const user1 = userFactory.create(1, '张三'); // 创建用户张三
const user2 = userFactory.create(1, '张三'); // 从缓存获取
console.log(user1 === user2); // true