深入设计模式第一节——单例模式

66 阅读1分钟

定义

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

优缺点

优点:避免频繁创建和销毁实例,减少内存占用等。 缺点:不适用动态扩展对象,或需创建多个相似对象的场景。

用途

  • 引用第三方库(多次引用只会使用一个库引用,如 jQuery)
  • 全局通用弹窗(如登录弹窗)
  • 全局状态管理(Vuex、Redux)等场景

基础例子

class Test {
  constructor() {
    if (Test.instance) {
      return Test.instance;
    }
    Test.instance = this;
  }
}
const t1 = new Test();
const t2 = new Test();
console.log('t1===t2', t1 === t2);

只有没有 Test.instance 才会走类的初始化,有 Test.instance 则直接返回已经初始化过的实例,从而保证实例是唯一的。

全局登录弹窗例子

import React from 'react';

const openLoginModal = (function () {
  let div;
  return function () {
    if (!div) {
      div = document.createElement('div');
      div.innerHTML = '登录弹窗';
      document.body.appendChild(div);
    }
    return div;
  };
})();

const Index = () => {
  return (
    <span
      onClick={() => {
        openLoginModal();
      }}
    >
      打开弹窗
    </span>
  );
};

export default Index;

上面的登录弹窗例子还存在一些代码设计上的问题:openLoginModal 既实现了单例的逻辑,也实现了创建登录弹窗的逻辑。这违反了单一职责原则,如果下次仍然需要创建另一个弹窗,我们无法复用创建弹窗的逻辑。所以我们需要将不变的部分抽离出来,编写一个通用单例创建的代理方法。

import React from 'react';

// 通用单例创建代理方法
const getSingle = function (fn) {
  let result;
  return function () {
    return result || (result = fn.apply(this, arguments));
  };
};

const openLoginModal = function () {
  const div = document.createElement('div');
  div.innerHTML = '登录弹窗';
  document.body.appendChild(div);
  return div;
};

const singleOpenLoginModal = getSingle(openLoginModal);

const Index = () => {
  return (
    <span
      onClick={() => {
        singleOpenLoginModal();
      }}
    >
      打开弹窗
    </span>
  );
};

export default Index;