设计模式对于解决特定的问题是有价值的,是软件开发人员在软件开发过程中面临的一般问题的典型解决方案。如果一个项目没有这些问题,就没有必要应用它们
单例模式
单例是可以实例化一次的类,并且可以全局访问。这个单个实例可以在我们的整个应用程序中共享,这使得单例模式非常适合管理应用程序中的全局状态。
首先,让我们看看使用 es2015实现的单例模式是什么样的。对于这个例子,我们将构建一个 Counter 类,它有:
- a
getInstance
method that returns the value of the instance - 返回实例的 getInstance 方法
- a
getCount
method that returns the current value of thecounter
variable - 返回计数器变量的当前值的 getCount 方法
- an
increment
method that increments the value ofcounter
by one - 将计数器的值增加1的递增方法
- a
decrement
method that decrements the value ofcounter
by one - 将计数器的值减一的递减方法
let counter = 0;
class Counter {
getInstance() {
return this;
}
getCount() {
return counter;
}
increment() {
return ++counter;
}
decrement() {
return --counter;
}
}
但是,这个类不符合单例模式的规范!一个单例应该只能实例化一次。目前,我们可以创建 Counter 类的多个实例。
let counter = 0;
class Counter {
getInstance() {
return this;
}
getCount() {
return counter;
}
increment() {
return ++counter;
}
decrement() {
return --counter;
}
}
const counter1 = new Counter();
const counter2 = new Counter();
console.log(counter1.getInstance() === counter2.getInstance()); // false
通过两次调用new方法,我们只需将 counter1和 counter2设置为等于不同的class实例。getInstance 方法在 counter1和 counter2上返回的值是对不同实例的引用: 它们并不严格相等!
让我们确保只能创建 Counter 类的一个实例。
确保只能创建一个实例的一种方法是创建一个名为 instance 的变量。在Counter的构造函数中,我们可以在创建新实例时将实例设置为对实例的引用。我们可以通过检查实例变量是否已经有一个值来防止新的实例化。如果是这种情况,那么实例已经存在,此时应该抛出一个错误,让用户知道
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 counter = new Counter();
const counter2 = new Counter();
// Error: You can only create one instance!
让我们从 Counter.js 文件中导出 Counter 实例。但是在这样做之前,我们也应该冻结实例。Freeze 方法确保使用代码不能修改实例。无法添加或修改冻结实例上的属性,这将减少意外覆盖实例上的值的风险。
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;
单例优点: 将实例化限制为一个实例可能会节省大量内存空间。不必每次都为新实例设置内存,我们只需为该实例设置内存,该实例将在整个应用程序中被引用。
单例缺点:单例的实例可以在整个应用程序中共享,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
React 中的状态管理
在React中,我们经常通过诸如 Redux 或 React Context 这样的状态管理工具来管理全局状态,而不是使用单例。尽管它们的全局状态行为看起来类似于单例,但是这些工具提供的是只读状态,而不是单例中的可变状态。当使用 Redux 时,只有纯函数reducers可以在组件通过dispatcher发送action之后更新状态。 虽然拥有全局状态的缺点不会通过使用这些工具而神奇地消失,但我们至少可以确保全局状态按照我们的意愿发生改变,因为组件不能直接更新状态。
本文搬运自www.patterns.dev/. Patterns.dev is free book on design patterns and component patterns for building powerful web apps with vanilla JavaScript and React.