Javascript设计模式

文章发布时间:

预计阅读时间:
11 分钟

前言

Javascript设计模式

在本文中,我们将探索一些常见的JavaScript设计模式,从简单到复杂,将每个模式的实现原理和使用场景进行详细说明。无论您是一个初学者还是一个有经验的开发人员,都可以从本文中学习到有价值的知识和技巧。设计模式是一项需要不断学习和实践的技能,通过掌握和应用设计模式,您将能够优化代码结构,提高代码质量,并提升自己在软件开发领域的技术能力。

每个设计模式都有其独特的用途和优势。本文将介绍以下几个设计模式:单例模式、工厂模式、观察者模式和策略模式。这些模式广泛应用于JavaScript开发中的各个领域,无论是前端开发、后端开发还是移动应用开发,都能发挥重要作用。

单例模式

单例模式是一种创建型设计模式,旨在确保一个类只有一个唯一的实例,并提供全局访问点以访问此实例。

概念

  1. 在JavaScript开发中,单例模式常用于以下情况:

    • 需要限制一个类只能拥有一个实例,以避免重复创建相同对象的多个实例,节省内存和资源。
    • 需要一个全局访问点,使得所有代码都可以方便地访问该实例。
  2. 单例模式通常通过以下方式实现:

    • 私有化构造函数:将类的构造函数设为私有,防止外部直接实例化。
    • 提供一个静态方法来获取实例:该方法负责在首次调用时创建实例,并返回实例对象。以后再次调用该方法时,直接返回已创建的实例。

举例

以下是一个简单的JavaScript单例模式的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Singleton {
constructor() {
// 构造函数私有化,防止外部直接实例化
// ...
}

static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}

// 单例类的其他方法和属性
// ...
}

// 使用示例
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // 输出: true,说明只有一个实例

总结

优点:

  • 确保一个类只有一个实例,节省了系统资源。
  • 提供了一个全局访问点,使得其他代码可以方便地访问该实例。

缺点:

  • 单例对象在全局范围内可见,可能会被错误地修改。
  • 单例模式在设计上可能会导致代码的耦合性增加,使代码难以扩展和测试。因此,在使用单例模式时,需要谨慎权衡其优缺点,并确保其适用于具体的场景和需求。

观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式。它通过定义一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。

概念

  1. 在观察者模式中,有两个核心角色:

    • Subject(主题):也称为被观察者或发布者,它维护着观察者列表,并提供了注册、删除和通知观察者的方法。主题在状态发生改变时,会遍历观察者列表,并调用每个观察者的相应方法进行更新。
    • Observer(观察者):观察者定义了接收并响应主题通知的方法。每个观察者都可以决定自己在收到通知时应该采取何种行动。
  2. 观察者模式适用于以下情况:

    • 当一个对象的改变需要其他对象也做出相应的改变时。
    • 当一个对象需要通知其他对象,但不需知道这些对象是谁。
    • 当对象之间的依赖关系动态变化,需要灵活地添加或移除观察者时。

观察者模式的优势在于实现了低耦合的对象之间的通信。主题只需知道观察者遵循的接口,而不需要知道具体的观察者实现。这样,在需求变更或新增观察者时,不需要修改主题的代码,只需添加新的观察者即可。

举例

以下是一个简单的JavaScript单例模式的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 观察者对象
class Observer {
constructor(name) {
this.name = name;
}

// 响应主题通知的方法
update(message) {
console.log(`${this.name}收到通知:${message}`);
}
}

// 主题对象
class Subject {
constructor() {
this.observers = [];
}

// 注册观察者
registerObserver(observer) {
this.observers.push(observer);
}

// 删除观察者
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}

// 通知观察者
notifyObservers(message) {
this.observers.forEach(observer => {
observer.update(message);
});
}
}

// 创建观察者和主题对象
const observer1 = new Observer("观察者1");
const observer2 = new Observer("观察者2");

const subject = new Subject();

// 注册观察者
subject.registerObserver(observer1);
subject.registerObserver(observer2);

// 发送通知给观察者
subject.notifyObservers("Hello 观察者们!");

// 输出:
// 观察者1收到通知:Hello 观察者们!
// 观察者2收到通知:Hello 观察者们!

总结

优点:

  • 松耦合:观察者模式可以将主题对象和观察者对象解耦,它们只需要通过抽象接口进行通信,互不依赖。
  • 可扩展:可以简单地添加新的观察者对象,无需修改现有代码。
  • 可重用性:多个观察者对象可以被复用于不同的主题对象。

缺点:

  • 通知顺序不确定性:如果有多个观察者对象订阅了同一个主题对象,它们收到通知的顺序在某些情况下可能是不确定的。
  • 增加复杂性:观察者模式引入了一些额外的抽象层,可能增加系统的复杂性。
  • 观察者数目限制:观察者模式中,主题对象需要维护一个观察者列表,如果观察者数量较大,可能会对系统的性能产生一定影响。

总的来说,观察者模式在实现松耦合、可扩展性和可重用性方面具有优势,但在通知顺序、复杂性和观察者数目方面存在一些限制和缺点。在实际应用中,需要根据具体情况综合考虑使用观察者模式的优缺点。

工厂模式

工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的方式,而无需暴露对象的具体实现细节给客户端。

概念

工厂模式通过定义一个公共的接口或抽象类来创建对象,而具体的对象创建逻辑则被封装在工厂类中。客户端只需要通过工厂类创建对象,而无需关心对象的具体实现过程。

  1. 工厂模式主要包括以下几个角色:

    • 抽象工厂(Abstract Factory):定义了创建产品对象的接口,客户端通过它调用具体工厂的方法创建对象。
    • 具体工厂(Concrete Factory):实现了抽象工厂接口,并负责创建具体的产品对象。
    • 抽象产品(Abstract Product):定义了产品的接口,客户端通过它和具体产品进行交互。
    • 具体产品(Concrete Product):实现了抽象产品接口,具体的产品对象由具体工厂创建。
  2. 工厂模式的适用场景:

    • 当客户端无需知道对象的具体实现,只需使用对象时,可以使用工厂模式隐藏对象创建的细节。
    • 当系统中存在多个具有同样接口的具体产品时,可以使用工厂模式统一创建和管理这些对象。
    • 当对象之间的依赖关系动态变化,需要灵活地添加或移除观察者时。

需要注意的是,工厂模式虽然提供了一种灵活、可扩展的对象创建方式,但也会增加系统的复杂度和代码量。在使用工厂模式时,需要根据具体场景综合考虑使用与维护的复杂度。

举例

以下是一个简单的JavaScript单例模式的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 定义抽象产品接口
class Product {
constructor(name) {
this.name = name;
}
introduce() {
console.log(`我是${this.name}`);
}
}

// 定义具体产品类
class ProductA extends Product {
constructor(name) {
super(name);
}
introduce() {
console.log(`我是产品A - ${this.name}`);
}
}

class ProductB extends Product {
constructor(name) {
super(name);
}
introduce() {
console.log(`我是产品B - ${this.name}`);
}
}

// 定义工厂类
class Factory {
createProduct(productType, name) {
switch (productType) {
case 'A':
return new ProductA(name);
case 'B':
return new ProductB(name);
default:
throw new Error('无法创建该类型的产品');
}
}
}

// 客户端代码
const factory = new Factory();
const productA = factory.createProduct('A', 'AAA');
const productB = factory.createProduct('B', 'BBB');

productA.introduce(); // 输出:我是产品A - AAA
productB.introduce(); // 输出:我是产品B - BBB

总结

优点:

  • 隐藏对象的具体创建过程,提供了一种解耦的方式,客户端无需关心对象的创建逻辑。
  • 可以通过工厂类统一管理对象的创建,方便集中控制和管理。
  • 符合开闭原则,易于扩展。通过增加新的具体工厂和具体产品,可以很方便地扩展系统功能。

缺点:

  • 维护成本高:由于工厂模式中涉及到多个工厂类和产品类的定义和创建,当需要新增或修改产品类时,可能需要修改多个工厂类和客户端代码。这会增加系统的维护成本,尤其在产品类较多、变化频繁的情况下更为明显。
  • 类型辨识困难:在一些复杂的工厂模式中,由于客户端无法直接使用具体产品类,而是通过工厂类创建产品对象,因此在使用产品时可能需要对具体产品的类型进行辨识和转换。这可能会引入一些类型判断的逻辑,增加了代码的复杂度。
  • 系统扩展困难:当需要新增一种产品类时,需要修改工厂类的代码,违背了开闭原则。对于已经存在的工厂类,不仅无法新增产品类,也无法删除产品类。对于已经存在的产品类,无法新增工厂类,也无法删除工厂类。这导致系统在扩展新功能时较为困难。
  • 运行效率低:在使用工厂模式时,由于需要通过工厂类来创建对象,会增加系统的运行负担。尤其在产品类较多时,可能会耗费较多的时间来查找正确的工厂类和产品类。
  • 增加了系统复杂度:引入了工厂类和产品类多层继承的结构,增加了系统的复杂度。对于小型系统或者简单的业务逻辑,使用工厂模式可能会显得过于复杂,不利于项目的开发和维护。

写在最后

至此,文章就分享完毕了,我是程序员LinXiao,一位前端开发工程师。

文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注
如果你对我有兴趣,欢迎添加我的个人微信Lxlucky2022
Githubhttps://github.com/KevinLin8