(四)工厂模式

工厂模式

  • 介绍
  • 演示
  • 场景
  • 总结

工厂模式 介绍

  • 将new操作单独封装
  • 遇到new时,就要考虑是否该使用工厂模式

概念

工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。著名的 Jive 论坛 ,就大量使用了工厂模式,工厂模式在 Java 程序系统可以说是随处可见。因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

示例

  • 你去购买汉堡,直接点餐、取餐,不会自己亲手做
  • 商店要封装做汉堡的工作,做好直接给买者

现在你要得到一个汉堡,你是跟服务员要(买)一个,还是自己动手做一个?这个问题,服务员就是工厂方法,而动手做一个其实就是new A()
另外从快餐店考虑,你想要提供一个汉堡,是让服务员(工厂方法)做出来(new A())给客户,还是让客户自己做一个汉堡?

从这个示例很容易理解工厂模式的用意,所有的设计模式都是很讲道理的,很容易理解

工厂模式 演示

常见的工厂模式的 UML 类图如下:

在这里插入图片描述

不过前端 JS 使用时,左侧的 Product 没有那么复杂。没有接口,也很少有多种子类,因此 UML 类图可简化为:

在这里插入图片描述

此处说明,之前讲过前端学习设计模式不能按照 java 的方式来,UML 类图也一样。设计模式要学以致用,因此得考虑 JS 语法和使用场景。

  • JS 没有接口,用继承的场景也不是特别多
  • JS 是弱类型语言

因此,将 UML 类图简化是很有必要的,要不然你会因为不懂 java 的语法和使用场景,而搞不清楚,反而达不到学习效果!!!谨记。

代码演示:

class Product {
    constructor(name) {
        this.name = name
    }
    init() {
        alert('init')
    }
    fun1() {
        alert('fn1')
    }
    fun2() {
        alert('fn2'}
}

class Creator {
    create(name) {
        return new Product(name)
    }
}

// 测试
let creator = new Creator()
let p = creator.create('p1')
p.init()
p.fn1()

对比刚开始的例子,Product就是汉堡,Creator就是服务员,很好理解吧。

但是在日常工作编码中,不会再额外增加Creator的类了,create直接当做静态的函数就行了。例如下一节的示例。

工厂模式 场景

工厂模式在日常使用非常多,凡是用到new的地方,都要考虑是否需要工厂模式。

jQuery - $('div')

  • $(‘div’)和new $(‘div’)有何区别?
  • 第一:书写麻烦,jQuery的链式操作将成为噩梦
  • 第二:一旦jQuery名字变化,将是灾难性的

模拟代码,$就是一个工厂

class jQuery {
    constructor(selector) {
        let slice = Array.prototype.slice
        let dom = slice.call(document.querySelectorAll(selector))
        let len = dom ? dom.length : 0
        for (let i = 0; i < len; i++) {
            this[i] = dom[i]
        }
        this.length = len
        this.selector = selector || ''
    }
    append(node) {

    }
    addClass(name) {

    }
    html(data) {

    }
    // 此处省略若干 API
}
window.$ = function (selector) {
    return new jQuery(selector)
}

做一个对比,如果开放给用户的不是$,然后让用户自己去new jQuery(selector),带来的问题:

  • 操作复杂,代码量增加,如$('div').append($('#p1')).html()这种操作将变得冗长繁琐
  • 一旦构造函数jQuery有变化,使用者都受牵连,不符合开闭原则

关于模式 jQuery 的源码,说一下解读经典框架的意义:

  • 第一,学习如何实现功能
  • 第二,学习它们的设计思路
  • 第三,强制自己写代码时模拟
  • 第四,自己写出优秀的代码

你只需要学会并且按照既有的模式来模仿,完全不需要你自己去创新。包括你看到的前端飞速进化的技术,也都不是创新,都是既有的经验。你日常开发所遇到的各种技术问题,100% 都有既有经验。

React.createElement

在 React 中经常使用 JSX 语法

var profile = <div>
  <img src="avatar.png" className="profile" />
  <h3>{[user.firstName, user.lastName].join(' ')}</h3>
</div>;

这是一种语法糖,编译之后就会是:

var profile = React.createElement("div", null,
    React.createElement("img", { src: "avatar.png", className: "profile" }),
    React.createElement("h3", null, [user.firstName, user.lastName].join(" "))
);

其实React.createElement也是一个工厂,模拟代码

class Vnode(tag, attrs, chilren) {
    // ...省略内部代码...
}
React.createElement =  function (tag, attrs, children) {
    return new Vnode(tag, attrs, chilren)
}

vue 中也用到了 vdom ,因此其中也有new vnode(...)的情况,和这个类似。

vue 异步组件

文档 https://cn.vuejs.org/v2/guide/components-dynamic-async.html

在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会被触发,且会把结果缓存起来供未来重渲染。例如:

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

如你所见,这个工厂函数会收到一个 resolve 回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用 reject(reason) 来表示加载失败。

此处不必深究使用和实现的细节,本节重点也不在于这个,而是在于工厂模式这种设计思路上。

工厂模式 - 总结

  • 工厂模式是什么?
  • 那种场景要考虑使用 —— new
  • 介绍的几个使用场景
  • 在日常写代码中,要试着去模仿、应用

设计原则验证:

  • 工造函数和创建者分离
  • 符合开放封闭原则
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值