为了更好的减少底层库变动对业务代码的影响,所以对于调用及操作场景的部分做了代码重构。 经历了两个多月的代码重构(实际也是新开一个项目),维护一个基础应用平台。
在重构的过程当中,使用了一些设计模式的理念,这里记录并回顾一下这些常用的设计模式

项目中的变化

Before

由于之前涉及到的巨多的关于三维场景的全局变量,目的在于方便各个操作方法中频繁的赋值及取值,但是造成了难以维护和混乱(无法跟踪何时赋值更新销毁等)

单例模式-before

Now

重构的时候使用封装了一个单例 为这些变量相关的操作提供了对应的函数,并且提供了这些变量的访问接口

单例模式的特点

  1. 由于模块化的导出,我们避免了构造函数 MethodsLibrary 的二次实例化
  2. 通过模块默认导出值提供了全局可访问的访问点
单例模式-now

单例模式的概念

什么是单例模式?如何能保证全局仅有一个实例呢?

  1. 一个类只有一个实例,并提供一个访问它的全局访问点

  2. 保证构造方法只能被 new 一次。通常就是将构造方法私有化并且需要判断是否已经创建过实例

class Single {
  constructor() {
    this.instance = null
  }

  static getInstance() {
    if (!this.instance) {
      this.instance = new Name()
    }
    return this.instance
  }
}
// 或者是闭包的形式
const Single = function () {}

Single.getInstance = (function () {
  let instance = null
  return function () {
    if (!instance) {
      instance = new Single()
    }
    return instance
  }
})()

// result 
const one = Single.getInstance()
const two = Single.getInstance()

console.log(one === two) // true

JS 中的单例模式

全局变量 - 最简单的单例模式

当然这只是一个完成定义上的单例,对于 JavaScript 而言,实际上一个全局声明的变量也可以当做单例来使用; 这个在之前的项目中频繁出现,也就是定义一个全局变量。

使用 模块化 或者 闭包 之后,我们避免了变量可能因为重复声明导致的命名空间的污染

// 闭包:将一些变量封装在函数内部,可以自定义接口与外界通讯
const user = (function () {
  let _name = '',
    _age = ''

  return {
    getUser() {
      return {
        name: _name,
        age: _age,
      }
    },
    setUser(args = { name: '', age: '' }) {
      _name = args.name
      _age = args.age
    },
  }
})()

/*-------------------*/

// ES6 - module
// tool.js
export const handleTools = () => {}
export const toolConfig = () => {}
// main.js
import { handleTools, toolConfig } from '@/util/tool'

惰性单例

当然了,重构项目中最主要的两个单例就是 MethodsLibraryVuex 提供的 Store 单例,都是项目运行就立刻创建,但是这里不影响我们说下惰性单例的好处(默认不创建,避免浪费一些资源)

<body>
  <button class="open"></button>
  <button class="close"></button>
</body>
<script>
  const Modal = (function () {
    let modal = null
    return function () {
      if (!modal) {
        modal = document.createElement('div')
        // add some attribute...
        document.body.appendChild(modal)
      }
      return modal
    }
  })()

  document.querySelector('.open').addEventListener('click', () => {
    const modal = new Modal() // 1.不点击就不会触发实例的创建,不需要创建多余的内容
    modal.style.display = 'block'
  })

  document.querySelector('.close').addEventListener('click', () => {
    const modal = new Modal() // 2. 获取到的依旧是那个已经被创建的实例
    modal.style.display = 'none'
  })
</script>