JavaScript设计模式之单例模式

279 阅读3分钟

什么是设计模式?

设计模式是一套已经被证明能够解决编程中常见问题的解决方案最佳实践。它们不特定于某种语言或技术,而是可以在各种上下文中应用。它们提供了可重用的解决常见问题的解决方案,并促进代码的可重用性、可维护性和可扩展性。

单例模式(Singleton Pattern )

在整个应用程序中共享单个全局实例。

用类实现单例模式

let instance;
let counter = 0;
 
class Counter {
  constructor() {
    if (instance) {
      throw new Error("You can only create one instance!");
    }
    instance = this;
  }
 
  getInstance() {
    return this;
  }
 
  getCount() {
    return counter;
  }
 
  increment() {
    return ++counter;
  }
 
  decrement() {
    return --counter;
  }
}

const singletonCounter = Object.freeze(new Counter());
export default singletonCounter;

  • instance变量:单例模式的实例只能实现一次,通过检查instance变量是否有引用来防止新的实例化。
  • Object.freeze:确保代码无法修改/添加/删除实例的属性。

将实例化限制为仅一个实例可能会节省大量内存空间。我们不必每次都为新实例设置内存,而只需为该实例设置内存,该内存在整个应用程序中都会引用。

用常规对象实现单例模式

用常规对象来实现相同的结果:

let count = 0;


const counter = {
  increment() {
    return ++count;
  },
  decrement() {
    return --count;
  }
};


Object.freeze(counter);
export { counter };

单例模式的缺点

  • 难以测试:在测试中,我们通常希望能够创建多个独立的实例,以便进行单元测试和模拟。但是,由于单例模式限制了实例的数量,这可能会导致测试困难。

  • 违反单一职责原则:单一职责原则是面向对象设计的五个基本原则(SOLID)之一。这个原则的主要思想是:一个类应该只有一个引起变化的原因。换句话说,一个类应该只负责一项职责。如果一个类有多于一个的职责,那么这些职责就会耦合在一起。当一个职责发生变化时,可能会影响到其他的职责。这会增加代码的复杂性,降低代码的可读性和可维护性。

  • 隐藏依赖关系:隐藏依赖关系是指一个类或模块依赖于另一个类或模块,但是只有在内部实现和运行时才会显现。

    例如,UserRepository类依赖于Database类,但这种依赖关系并没有在UserRepository的接口或构造函数中体现出来,而是隐藏在getUserById方法的内部实现中。

    public class Database {
        private static Database instance;
    
        public static Database getInstance() {
            if (instance == null) {
                instance = new Database();
            }
            return instance;
        }
    
        // 数据库操作方法...
    }
    
    public class UserRepository {
        public User getUserById(String id) {
            Database db = Database.getInstance();
            // 使用db查询用户...
        }
    }
    
  • 全局行为:由于单例模式提供了全局访问点,导致全局状态的存在。全局状态是指在不同部分之间共享的可变状态。这会导致代码的复杂性和不确定性增加,因为我们无法轻松追踪和控制这些全局状态的变化。

Redux、React Context状态管理工具

在 React 中,经常通过 Redux 或 React Context 等状态管理工具依赖全局状态。看起来很像单例模式,具备全局状态。但是并不一样,单例模式提供的可变状态,而它们提供的是只读状态,更改状态需要派发aciton纯函数行为。好处是,所有的状态变化都是可预测和可追踪的。因为所有的状态变化都通过同一种机制(action和reducer)进行,所以你可以准确地知道状态在何时、如何改变的。