8.前端设计模式-工厂模式-《前端知识进阶》

admin 2025-11-01 15:44:06 编程 来源:ZONE.CI 全球网 0 阅读模式
  • 1. 什么是工厂模式?
  • 2. 工厂模式的实现方式
    • (1)简单工厂模式
    • (2)工厂方法模式
    • (3)抽象工厂模式
  • 3. 工厂模式的通用实现
  • 4. Vue中的工厂模式
    • (1)VNode
    • (2)vue-router
  • 5. 工厂模式的优缺点
  • 6. 工厂模式的使用场景

    1. 什么是工厂模式?

    工厂模式就是根据不用的输入返回不同的实例,一般用来创建同一类对象,它的主要思想就是将对象的创建与对象的实现分离

    在创建对象时,不暴露具体的逻辑,而是将逻辑封装在函数中,那么这个函数就可以被视为一个工厂。工厂模式根据抽象程度的不同可以分为:简单工厂、工厂方法、抽象工厂

    一个简单的例子,我们去餐馆吃饭,只需要按照菜单上的菜名进行点餐,然后菜做出来之后,不需要知道这些菜是怎么做的,只管吃就好了。在这里面,餐馆就相当于工厂,负责生产菜品,访问者通过餐馆就可以拿到产品。

    这个例子有一个特点,访问者只需要产品名就可以从工厂获得实例,访问者不需要关心实例创建的过程。工厂模式其实就是将创建对象的过程单独封装,它的目的,就是为了实现无脑传参。

    2. 工厂模式的实现方式

    下面就分别看一下简单工厂模式、工厂方法模式、抽象工厂模式这三种工厂模式的实现方式。

    (1)简单工厂模式

    简单工厂模式又叫静态工厂模式,由一个工厂对象决定创建某一种产品对象类的实例。主要用来创建同一类对象。

    下面来看一个权限管理的例子,需要根据用户的权限进行页面的渲染。所以,在不同用户权限等级的构造函数中,需要保存该用户可以访问到的页面,再根据权限进行实例化用户。

    在ES6中,这里不再使用构造函数来创建对象,而是使用ES6中的class关键字来创建类,并使用static关键字将简单工厂封装到User类的静态方法下:

    1. //User类
    2. class User {
    3. //构造器
    4. constructor(opt) {
    5. this.name = opt.name;
    6. this.viewPage = opt.viewPage;
    7. }
    8. //静态方法
    9. static getInstance(role) {
    10. switch (role) {
    11. case 'superAdmin':
    12. return new User({ name: '超级管理员', viewPage: ['首页', '应用数据', '权限管理'] });
    13. break;
    14. case 'admin':
    15. return new User({ name: '管理员', viewPage: ['首页', '应用数据'] });
    16. break;
    17. case 'user':
    18. return new User({ name: '普通用户', viewPage: ['首页'] });
    19. break;
    20. default:
    21. throw new Error('参数错误, 可选参数:superAdmin、admin、user')
    22. }
    23. }
    24. }
    25. // 实例化对象
    26. let superAdmin = User.getInstance('superAdmin');
    27. let admin = User.getInstance('admin');
    28. let normalUser = User.getInstance('user');

    User就是一个简单工厂,在该函数中有3个实例中分别对应不同的权限的用户。当调用工厂函数时,只需要传递superAdmin, admin, user这三个可选参数中的一个获取对应的实例对象。

    简单工厂模式的优势就在于,只需要一个参数,就可以获得所需的对象,无需知道对象创建的具体细节。但是,在函数内部包含了对象所有的创建逻辑,和判断逻辑的代码,如果判断逻辑很多,或者代码逻辑很复杂,这样工厂函数就会变的很复杂,很庞大,难以维护。所以,简单工厂只适合以下情况:

    • 创建的对象数量较少;
    • 创建的对象的逻辑不是很复杂。

      (2)工厂方法模式

      工厂方法模式本意是将实际创建对象的工作放在子类中,这样核心类就变成了抽象类。但是在JavaScript中,我们无法像传统面向对象语言那样去实现创建类,所以,只要遵循它的主要思想即可。

    虽然ES6也没有实现abstract,但是可以使用new.target来模拟出抽象类。new.target指向直接被new执行的构造函数,对new.target进行判断,如果指向了该类则抛出错误来使得该类成为抽象类。

    new.target属性允许你检测函数或构造方法是否是通过new运算符被调用的。在通过new运算符被初始化的函数或构造方法中,new.target返回一个指向构造方法或函数的引用。在普通的函数调用中,new.target 的值是undefined。

    在上面的简单工厂模式中。每次添加一个构造函数都要修改两处代码,现在对它加以改造:

    1. class User {
    2. constructor(name = '', viewPage = []) {
    3. if(new.target === User) {
    4. throw new Error('抽象类不能实例化!');
    5. }
    6. this.name = name;
    7. this.viewPage = viewPage;
    8. }
    9. }
    10. class UserFactory extends User {
    11. constructor(name, viewPage) {
    12. super(name, viewPage)
    13. }
    14. create(role) {
    15. switch (role) {
    16. case 'superAdmin':
    17. return new UserFactory( '超级管理员', ['首页', '应用数据', '权限管理'] );
    18. break;
    19. case 'admin':
    20. return new User({ name: '管理员', viewPage: ['首页', '应用数据'] });
    21. break;
    22. case 'user':
    23. return new UserFactory( '普通用户', ['首页'] );
    24. break;
    25. default:
    26. throw new Error('参数错误, 可选参数:superAdmin、admin、user')
    27. }
    28. }
    29. }
    30. let userFactory = new UserFactory();
    31. let superAdmin = userFactory.create('superAdmin');
    32. let admin = userFactory.create('admin');
    33. let user = userFactory.create('user');

    工厂方法可以看做是一个实例化对象的工厂,它需要做的就是实例化对象。

    (3)抽象工厂模式

    上面两种方式都是直接生成实例,而抽象工厂模式并不能直接生成实例,而是用于产品类簇的创建

    在网站登录中,上面实例中的user可能使用不用的第三方登录,例如微信、QQ、微博,这三类账号就是对应的类簇。在抽象工厂中,类簇一般用于父类的定义,并在父类中定义一些抽象的方法(声明但不能使用的方法),在通过抽象工厂让子类继承父类,所以,抽象工厂实际上就是实现子类继承父类的方法。

    在传统面向对象的语言中常用abstract进行声明,但是在JavaScript中,abstract是属于保留字,可以通过在类的方法中抛出错误来模拟抽象类。

    1. function getAbstractUserFactory(type) {
    2. switch (type) {
    3. case 'wechat':
    4. return UserOfWechat;
    5. break;
    6. case 'qq':
    7. return UserOfQq;
    8. break;
    9. case 'weibo':
    10. return UserOfWeibo;
    11. break;
    12. default:
    13. throw new Error('参数错误, 可选参数:wechat、qq、weibo')
    14. }
    15. }
    16. let WechatUserClass = getAbstractUserFactory('wechat');
    17. let QqUserClass = getAbstractUserFactory('qq');
    18. let WeiboUserClass = getAbstractUserFactory('weibo');
    19. let wechatUser = new WechatUserClass('微信张三');
    20. let qqUser = new QqUserClass('QQ张三');
    21. let weiboUser = new WeiboUserClass('微博张三');

    总结:

    • 简单工厂模式又叫静态工厂方法,用来创建某一种产品对象的实例,用来创建单一对象;
    • 工厂方法模式是将创建实例推迟到子类中进行;
    • 抽象工厂模式是对类的工厂抽象用来创建产品类簇,不负责创建某一类产品的实例。

    在实际的业务中,需要根据实际的业务复杂度来选择合适的模式。对于非大型的前端应用来说,灵活使用简单工厂其实就能解决大部分问题。

    3. 工厂模式的通用实现

    注意: 这里所说的工厂模式是指简单工厂模式。在工厂模式中,主要有两个重要的概念:

    • Factory :工厂,负责返回产品实例
    • Product :产品,访问者从工厂拿到产品实例

    其结构如下:工厂模式 - 图1下面用通用的方法实现,这里直接用 class 语法:

    1. /* 工厂类 */
    2. class Factory {
    3. static getInstance(type) {
    4. switch (type) {
    5. case 'Product1':
    6. return new Product1()
    7. case 'Product2':
    8. return new Product2()
    9. default:
    10. throw new Error('当前没有这个产品')
    11. }
    12. }
    13. }
    14. /* 产品类1 */
    15. class Product1 {
    16. constructor() { this.type = 'Product1' }
    17. operate() { console.log(this.type) }
    18. }
    19. /* 产品类2 */
    20. class Product2 {
    21. constructor() { this.type = 'Product2' }
    22. operate() { console.log(this.type) }
    23. }
    24. const prod1 = Factory.getInstance('Product1')
    25. prod1.operate() // 输出: Product1
    26. const prod2 = Factory.getInstance('Product3') // 输出: Error 当前没有这个产品

    需要注意,由于 JavaScript 很灵活,简单工厂模式返回的产品对象不一定非要是类实例,也可以是字面量形式的对象,所以可以根据场景灵活选择返回的产品对象形式。

    4. Vue中的工厂模式

    在Vue中,很多地方也是用到了工厂模式,下面来看下其中的两个例子。

    (1)VNode

    和原生的 document.createElement 类似,Vue 这种具有虚拟 DOM 树(Virtual Dom Tree)机制的框架在生成虚拟 DOM 的时候,提供了 createElement 方法用来生成 VNode,用来作为真实 DOM 节点的映射:

    1. createElement('h3', { class: 'main-title' }, [
    2. createElement('img', { class: 'avatar', attrs: { src: '../avatar.jpg' } }),
    3. createElement('p', { class: 'user-desc' }, 'hello world')
    4. ])

    createElement 函数结构大概如下:

    1. class Vnode (tag, data, children) { ... }
    2. function createElement(tag, data, children) {
    3. return new Vnode(tag, data, children)
    4. }

    可以看到,createElement 函数内会进行 VNode 的具体创建,创建的过程是很复杂的,而框架提供的 createElement 工厂方法封装了复杂的创建与验证过程,对于使用者来说就很方便了。

    (2)vue-router

    在Vue在路由创建模式中,也多次用到了工厂模式:

    1. xport default class VueRouter {
    2. constructor(options) {
    3. this.mode = mode // 路由模式
    4. switch (mode) { // 简单工厂
    5. case 'history': // history 方式
    6. this.history = new HTML5History(this, options.base)
    7. break
    8. case 'hash': // hash 方式
    9. this.history = new HashHistory(this, options.base, this.fallback)
    10. break
    11. case 'abstract': // abstract 方式
    12. this.history = new AbstractHistory(this, options.base)
    13. break
    14. default:
    15. // ... 初始化失败报错
    16. }
    17. }
    18. }

    在上面的代码中,mode 是路由创建的模式,这里有三种 History、Hash、Abstract,其中,History 是 H5 的路由方式,Hash 是路由中带 # 的路由方式,Abstract 代表非浏览器环境中路由方式,比如 Node、weex 等;this.history 用来保存路由实例,vue-router 中使用了工厂模式的思想来获得响应路由控制类的实例。

    Vue-Router没有把工厂方法的产品创建流程封装出来,而是直接将产品实例的创建流程暴露在 VueRouter 的构造函数中,在被 new 的时候创建对应产品实例,相当于 VueRouter 的构造函数就是一个工厂方法。

    如果一个系统不是单页面应用,而是多页面应用,那么就需要创建多个 VueRouter 的实例,此时 VueRouter 的构造函数也就是工厂方法将会被多次执行,以分别获得不同实例。

    5. 工厂模式的优缺点

    工厂模式将对象的创建和实现进行了分离,其优点如下:

    • 良好的封装,代码结构清晰,访问者无需知道对象的创建流程,特别是创建比较复杂的情况下;
    • 扩展性优良,通过工厂方法隔离了用户和创建流程隔离,符合开放封闭原则;
    • 解耦了高层逻辑和底层产品类,符合最少知识原则,不需要的就不要去交流;

    工厂模式的缺点如下:带来了额外的系统复杂度,增加了抽象性;

    6. 工厂模式的使用场景

    那么什么时候使用工厂模式呢:

    • 对象的创建比较复杂,而访问者无需知道创建的具体流程;
    • 处理大量具有相同属性的小对象;
    以太坊cppgolang区别 编程

    以太坊cppgolang区别

    以太坊是一种去中心化的开源平台,它采用智能合约技术,旨在构建和运行不受干扰的分布式应用程序。作为目前最受欢迎的区块链平台之一,以太坊提供了多种编程语言的支持,其
    progolang 编程

    progolang

    Go语言(Golang)是由Google开发的一门静态类型编程语言。作为一名专业的Golang开发者,我深知这门语言的优势和特点。在本文中,我将介绍Golang
    golangn个发送者 编程

    golangn个发送者

    Golang是一种开源的编程语言,由Google团队开发,旨在提高程序的并发性和简化软件开发过程。在Go语言中,有时需要向多个接收者发送信息。本文将介绍如何在G
    golang技能图谱 编程

    golang技能图谱

    从互联网行业的快速发展到人工智能技术的日益成熟,各种编程语言也应运而生。而在这众多的编程语言中,Golang(即Go)作为一门强大且高效的开发语言备受关注。Go
    评论:0   参与:  4