JavaScript设计模式 -- 工厂模式
在实际开发中,我们经常会遇到需要根据不同场景生成不同对象的情况。为了解决对象创建与使用之间的耦合问题,工厂模式应运而生。本文将以 JavaScript 为例,从简单工厂到工厂方法,再到抽象工厂,介绍工厂模式的多种实现方式,并通过多个场景的示例代码,帮助大家更好地理解和应用这一设计模式。
工厂模式简介
工厂模式属于创建型设计模式,其核心思想是将对象的创建过程封装起来,让调用者只需关注产品接口,而无需了解具体实现。这样可以降低系统各模块之间的耦合度,使得扩展和维护更加方便。
根据具体需求,工厂模式常见的变体包括:
- 简单工厂模式:由一个工厂类根据参数创建不同的产品实例;
- 工厂方法模式:通过定义抽象工厂接口,让子类决定实例化哪一个具体产品;
- 抽象工厂模式:用于创建一系列相关或相互依赖的产品。
接下来,我们分别介绍这些模式在 JavaScript 中的实现,并结合多种场景给出示例。
简单工厂模式
简单工厂模式将产品的创建逻辑集中在一个工厂方法中,根据传入参数返回相应的产品实例。适用于产品种类较少、逻辑简单的场景。
示例 1:产品选择
假设我们需要根据用户输入创建不同的产品对象,可以这样实现:
// 定义具体产品类
class ProductA {
use() {
console.log('使用产品 A');
}
}
class ProductB {
use() {
console.log('使用产品 B');
}
}
// 简单工厂类
class SimpleFactory {
static createProduct(type) {
switch (type.toLowerCase()) {
case 'a':
return new ProductA();
case 'b':
return new ProductB();
default:
throw new Error(`不支持的产品类型: ${type}`);
}
}
}
// 客户端调用
const product = SimpleFactory.createProduct('a');
product.use(); // 输出:使用产品 A
示例 2:几何图形
在图形绘制应用中,我们可能需要生成不同的几何图形对象,如圆形、正方形和三角形。简单工厂可以帮助我们根据形状名称动态创建对应实例。
// 定义图形基类
class Shape {
draw() {
throw new Error('抽象方法不能直接调用');
}
}
class Circle extends Shape {
draw() {
console.log('绘制圆形');
}
}
class Square extends Shape {
draw() {
console.log('绘制正方形');
}
}
class Triangle extends Shape {
draw() {
console.log('绘制三角形');
}
}
// 图形工厂
class ShapeFactory {
static createShape(shapeType) {
switch (shapeType.toLowerCase()) {
case 'circle':
return new Circle();
case 'square':
return new Square();
case 'triangle':
return new Triangle();
default:
throw new Error(`未知的图形类型: ${shapeType}`);
}
}
}
// 客户端调用
const shape = ShapeFactory.createShape('circle');
shape.draw(); // 输出:绘制圆形
工厂方法模式
工厂方法模式通过引入工厂接口,让子类负责具体对象的创建,从而更灵活地扩展产品。每个工厂子类只负责创建一种产品,符合单一职责原则。
示例 1:创建角色
考虑一个游戏场景,我们需要创建不同种类的角色,比如战士和法师。每种角色的创建逻辑可能不尽相同,这时就可以采用工厂方法模式。
// 定义角色基类
class Character {
attack() {
throw new Error('抽象方法不能直接调用');
}
}
class Warrior extends Character {
attack() {
console.log('战士挥剑攻击');
}
}
class Mage extends Character {
attack() {
console.log('法师施放魔法');
}
}
// 定义角色工厂基类
class CharacterFactory {
createCharacter() {
throw new Error('抽象方法不能直接调用');
}
}
// 具体工厂:战士工厂
class WarriorFactory extends CharacterFactory {
createCharacter() {
return new Warrior();
}
}
// 具体工厂:法师工厂
class MageFactory extends CharacterFactory {
createCharacter() {
return new Mage();
}
}
// 客户端调用
const warriorFactory = new WarriorFactory();
const warrior = warriorFactory.createCharacter();
warrior.attack(); // 输出:战士挥剑攻击
const mageFactory = new MageFactory();
const mage = mageFactory.createCharacter();
mage.attack(); // 输出:法师施放魔法
示例 2:生成通知
在一个消息系统中,我们可能需要生成不同类型的通知,比如邮件通知和短信通知。采用工厂方法模式,可以为每种通知定义一个专属工厂。
// 定义通知基类
class Notification {
send(message) {
throw new Error('抽象方法不能直接调用');
}
}
class EmailNotification extends Notification {
send(message) {
console.log(`通过邮件发送通知:${message}`);
}
}
class SMSNotification extends Notification {
send(message) {
console.log(`通过短信发送通知:${message}`);
}
}
// 定义通知工厂基类
class NotificationFactory {
createNotification() {
throw new Error('抽象方法不能直接调用');
}
}
// 具体工厂:邮件通知工厂
class EmailNotificationFactory extends NotificationFactory {
createNotification() {
return new EmailNotification();
}
}
// 具体工厂:短信通知工厂
class SMSNotificationFactory extends NotificationFactory {
createNotification() {
return new SMSNotification();
}
}
// 客户端调用
const emailFactory = new EmailNotificationFactory();
const emailNotification = emailFactory.createNotification();
emailNotification.send('您的订单已发货'); // 输出:通过邮件发送通知:您的订单已发货
const smsFactory = new SMSNotificationFactory();
const smsNotification = smsFactory.createNotification();
smsNotification.send('您的验证码是123456'); // 输出:通过短信发送通知:您的验证码是123456
抽象工厂模式
抽象工厂模式用于创建一系列相关或相互依赖的对象,而不需要指定它们具体的类。它特别适合产品族(family of products)的场景,比如跨平台 UI、主题切换等。
示例 1:UI 组件族
假设我们需要开发一个跨平台应用,根据操作系统不同生成一套不同风格的 UI 组件(按钮、输入框等)。使用抽象工厂可以统一管理不同平台的组件族。
// 定义产品:按钮和输入框的抽象接口
class Button {
render() {
throw new Error('抽象方法不能直接调用');
}
}
class TextField {
render() {
throw new Error('抽象方法不能直接调用');
}
}
// Windows 平台的具体组件
class WindowsButton extends Button {
render() {
console.log('渲染 Windows 风格按钮');
}
}
class WindowsTextField extends TextField {
render() {
console.log('渲染 Windows 风格输入框');
}
}
// macOS 平台的具体组件
class MacButton extends Button {
render() {
console.log('渲染 macOS 风格按钮');
}
}
class MacTextField extends TextField {
render() {
console.log('渲染 macOS 风格输入框');
}
}
// 抽象工厂接口
class UIFactory {
createButton() {
throw new Error('抽象方法不能直接调用');
}
createTextField() {
throw new Error('抽象方法不能直接调用');
}
}
// Windows 平台工厂
class WindowsUIFactory extends UIFactory {
createButton() {
return new WindowsButton();
}
createTextField() {
return new WindowsTextField();
}
}
// macOS 平台工厂
class MacUIFactory extends UIFactory {
createButton() {
return new MacButton();
}
createTextField() {
return new MacTextField();
}
}
// 客户端调用
function renderUI(factory) {
const button = factory.createButton();
const textField = factory.createTextField();
button.render();
textField.render();
}
// 根据当前平台选择工厂
const currentPlatform = 'mac'; // 或者 'windows'
const uiFactory = currentPlatform === 'windows' ? new WindowsUIFactory() : new MacUIFactory();
renderUI(uiFactory);
// 输出(macOS 平台):
// 渲染 macOS 风格按钮
// 渲染 macOS 风格输入框
示例 2:跨平台资源加载
在某些应用中,我们可能需要根据运行环境加载不同的资源(如配置文件、语言包等)。抽象工厂模式可以帮助我们创建一系列相关的资源加载器。
// 定义语言包加载器的接口
class LanguagePack {
load() {
throw new Error('抽象方法不能直接调用');
}
}
class ConfigLoader {
load() {
throw new Error('抽象方法不能直接调用');
}
}
// 英文资源
class EnglishLanguagePack extends LanguagePack {
load() {
console.log('加载英文语言包');
}
}
class EnglishConfigLoader extends ConfigLoader {
load() {
console.log('加载英文配置文件');
}
}
// 中文资源
class ChineseLanguagePack extends LanguagePack {
load() {
console.log('加载中文语言包');
}
}
class ChineseConfigLoader extends ConfigLoader {
load() {
console.log('加载中文配置文件');
}
}
// 抽象工厂接口
class ResourceFactory {
createLanguagePack() {
throw new Error('抽象方法不能直接调用');
}
createConfigLoader() {
throw new Error('抽象方法不能直接调用');
}
}
// 英文资源工厂
class EnglishResourceFactory extends ResourceFactory {
createLanguagePack() {
return new EnglishLanguagePack();
}
createConfigLoader() {
return new EnglishConfigLoader();
}
}
// 中文资源工厂
class ChineseResourceFactory extends ResourceFactory {
createLanguagePack() {
return new ChineseLanguagePack();
}
createConfigLoader() {
return new ChineseConfigLoader();
}
}
// 客户端调用
const locale = 'zh'; // 或者 'en'
const resourceFactory = locale === 'zh' ? new ChineseResourceFactory() : new EnglishResourceFactory();
resourceFactory.createLanguagePack().load(); // 输出:加载中文语言包
resourceFactory.createConfigLoader().load(); // 输出:加载中文配置文件
总结与应用场景
工厂模式通过封装对象创建逻辑,使得代码具有更好的解耦性和扩展性。下面是各个模式的主要特点及适用场景:
-
简单工厂模式
- 特点:通过一个静态方法创建不同产品,适合产品种类少、逻辑简单的情况。
- 应用场景:工具类、简单配置管理等。
-
工厂方法模式
- 特点:将创建对象的责任交给子类,符合单一职责原则,便于扩展。
- 应用场景:不同业务逻辑下的对象创建,如角色生成、通知系统等。
-
抽象工厂模式
- 特点:一次性创建一系列相关产品,确保产品族之间的一致性。
- 应用场景:跨平台 UI、主题切换、资源加载等需要整体解决方案的场景。
通过本文提供的多种示例,希望能帮助你理解如何在不同场景下灵活使用工厂模式。无论是简单的对象创建还是复杂产品族的管理,工厂模式都能提供一种优雅的解决方案,使系统更易维护、扩展和测试。
欢迎在评论区分享你的使用心得与疑问!