当前位置: 首页 > article >正文

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、主题切换、资源加载等需要整体解决方案的场景。

通过本文提供的多种示例,希望能帮助你理解如何在不同场景下灵活使用工厂模式。无论是简单的对象创建还是复杂产品族的管理,工厂模式都能提供一种优雅的解决方案,使系统更易维护、扩展和测试。

欢迎在评论区分享你的使用心得与疑问!


http://www.kler.cn/a/542464.html

相关文章:

  • Docker Compose 容器卷映射:是否需要提前将文件拷贝到宿主机?
  • 【报错解决】Sql server 2022连接数据库时显示证书链是由不受信任的颁发机构颁发的
  • Vue(7)
  • 如何把邮件批量导出到本地
  • [前端]CRX持久化
  • 脚手架开发【实战教程】prompts + fs-extra
  • 五、OSG学习笔记-矩阵变换
  • 25考研材料复试面试常见核心问题真题汇总,材料考研复试面试有哪些经典问题?材料考研复试过程最看重什么内容?
  • python C# 内存共享互传 图像 以及字符串
  • 蓝桥杯 Java B 组 - 第 1 周复习总结
  • 3、k8s项目的生命周期和yaml文件
  • uniapp商城之登录模块
  • 《深度学习》——CNN卷积神经网络模型及项目实例
  • 【Prometheus】MySQL主从搭建,以及如何通过prometheus监控MySQL运行状态
  • FTP(File Transfer Protocol)-文件传输协议
  • C++引用深度详解
  • Unity做2D小游戏5------多个动画互相转换
  • docker配置国内源
  • 【unity实战】实现摄像机跟随效果
  • 【AI知识点】大模型开源的各种级别和 deepseek 的开源级别
  • Java 大视界 -- 5G 与 Java 大数据融合的行业应用与发展趋势(82)
  • ArcGIS Pro SDK (二十六)自定义配置
  • 基于 PyTorch 的树叶分类任务:从数据准备到模型训练与测试
  • 25考研电子信息复试面试常见核心问题真题汇总,电子信息考研复试没有项目怎么办?电子信息考研复试到底该如何准备?
  • 进阶版MATLAB 3D柱状图
  • 【系统架构设计师】操作系统 - 进程管理 ① ( 进程概念 | 进程组成 | 进程 与 程序 | 进程 与 线程 | 线程 可共享的资源 - ☆考点 )