设计模式(三)——创建型:单例模式-数据管理哲学

设计模式(三)——创建型:单例模式-数据管理哲学

·

2 min read

单例模式的实现思路

单例模式就是保证一个类仅有一个实例,并提供一个访问它的全局访问点。

一般情况下,当我们创建了一个类后,可以通过new关键字调用构造函数进而生成任意多的实例对象。那我们该如何保证一个类仅有一个实例?其实要做到这一点,就需要构造函数具备判断自己能否已经创建过一个实例的能力。

class SingleDog {
  static instance: SingleDog;

  show() {
    console.log("我是一个单例对象");
  }

  static getInstance() {
    // 判断是否已经new过一个实例
    if (!SingleDog.instance) {
      // 若这个唯一的实例不存在,那么先创建它
      SingleDog.instance = new SingleDog();
    }
    // 如果这个唯一的实例已经存在,则直接返回
    return SingleDog.instance;
  }
}

const s1 = SingleDog.getInstance();
const s2 = SingleDog.getInstance();

// true
console.log(s1 === s2);

同样,getInstance的逻辑我们也可以用闭包进行实现。

class SingleDog {
  static getInstance: any;
  show() {
    console.log("我是一个单例对象");
  }
}

SingleDog.getInstance = (function () {
  // 自定已有变量instance,模拟私有变量
  let instance: SingleDog | null = null;
  return function () {
    // 判断自由变量是否为null
    if (!instance) {
      // 如果为null 则new出唯一实例
      instance = new SingleDog();
    }
    return instance;
  };
})();

const s1 = SingleDog.getInstance;
const s2 = SingleDog.getInstance;

// true
console.log(s1 === s2);

vuex中的单例模式

近年来,基于Flux架构的状态管理工具层出不穷,其中应用最广泛的应该是Redux和Vuex。无论是Redux和Vuex,他们都实现了一个全局的Store用于存储应用的所有状态。这个Store的实现,正是单例模式的典型应用。

当组件非常多,组件间关系复杂,嵌套层级很深的时候,原始的组件通信方式会使我们的逻辑变得复杂难以维护。这时最好的做法是将共享的数据抽出来,放在全局,供组件们按照一定规则去存取数据,保证状态以一种可预测的方式发生变化。于是便有了Vuex,这个用来存放共享数据的唯一数据源就是Store。

通过调用Vue.use()方法,我们安装了Vuex插件。Vuex插件是一个对象,它在内部实现了一个install方法,这个方法会在插件安装时被调用,从而把Store注入到Vue实例中。也就是说每install一次,都会尝试给Vue实例注入一个Store。

在install方法里,有一段逻辑和getInstance非常相似。

let Vue // 这个Vue的作用和楼上的instance作用一样
...

export function install (_Vue) {
  // 判断传入的Vue实例对象是否已经被install过Vuex插件(是否有了唯一的state)
  if (Vue && _Vue === Vue) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  // 若没有,则为这个Vue实例对象install一个唯一的Vuex
  Vue = _Vue
  // 将Vuex的初始化逻辑写进Vue的钩子函数里
  applyMixin(Vue)
}

实现一个全局的模态框

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单例模式弹框</title>
</head>
<style>
    #modal {
        height: 200px;
        width: 200px;
        line-height: 200px;
        position: fixed;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        border: 1px solid black;
        text-align: center;
    }
</style>
<body>
    <button id='open'>打开弹框</button>
    <button id='close'>关闭弹框</button>
</body>
<script>
    // 核心逻辑,这里采用了闭包思路来实现单例模式
    const Modal = (function() {
        let modal = null
        return function() {
            if(!modal) {
                modal = document.createElement('div')
                modal.innerHTML = '我是一个全局唯一的Modal'
                modal.id = 'modal'
                modal.style.display = 'none'
                document.body.appendChild(modal)
            }
            return modal
        }
    })()

    // 点击打开按钮展示模态框
    document.getElementById('open').addEventListener('click', function() {
        // 未点击则不创建modal实例,避免不必要的内存占用;此处不用 new Modal 的形式调用也可以,和 Storage 同理
        const modal = new Modal()
        modal.style.display = 'block'
    })

    // 点击关闭按钮隐藏模态框
    document.getElementById('close').addEventListener('click', function() {
        const modal = new Modal()
        if(modal) {
            modal.style.display = 'none'
        }
    })
</script>
</html>