前端设计模式

面向对象 OOP 和 UML 类图 - 前端开发的必备编程思想

当谈到面向对象编程(Object-Oriented Programming,OOP)时,它是一种编程范式,其中程序的结构是基于对象的概念。在面向对象编程中,问题被分解为一组相互作用的对象,每个对象都有自己的状态(属性)和行为(方法)。这种方式使得代码更加模块化、可重用和易于理解。

面向对象编程中的核心概念包括:

  1. 类(Class):类是对象的蓝图或模板,描述了对象的属性和方法。它定义了对象的共同特征和行为。例如,可以定义一个名为"Person"的类,它具有属性(如姓名、年龄)和方法(如说话、行走)。
  2. 对象(Object):对象是类的实例,它具有类定义的属性和方法。通过实例化类,可以创建对象。例如,可以通过实例化"Person"类创建一个名为"John"的对象,它具有特定的姓名和年龄,并可以执行相应的方法。
  3. 封装(Encapsulation):封装是将数据和操作封装在一个单元(类)中,以实现信息隐藏和保护数据的安全性。通过定义类的公共接口(方法)来访问和操作对象的数据,而对于类的内部实现细节则是私有的。
  4. 继承(Inheritance):继承是一种机制,允许一个类继承另一个类的属性和方法。通过继承,子类可以重用父类的代码,并可以在不修改父类的情况下添加自己的特定行为。
  5. 多态(Polymorphism):多态是指同一操作对不同对象的不同响应方式。通过多态,可以使用统一的接口来处理不同类型的对象,从而提高代码的灵活性和可扩展性。

UML(Unified Modeling Language)类图是一种用于可视化和描述类、对象、关系和行为的图形化表示方法。它是一种常用的软件工程工具,用于设计和分析系统。在类图中,可以表示类之间的关系(如继承、关联、聚合等)和类的属性和方法。

类图中的一些常见元素包括:

  1. 类(Class):用矩形框表示类,包含类的名称、属性和方法。
  2. 属性(Attribute):表示类的状态或特征,通常以名称和类型的形式表示。
  3. 方法(Method):表示类的行为或操作,通常以名称和参数列表的形式表示。
  4. 关联关系(Association):表示类之间的关联关系,可以是单向或双向的。
  5. 继承关系(Inheritance):表示一个类继承另一个类的关系,通常用箭头指向父类。
  6. 聚合关系(Aggregation):表示一种弱的关联关系,表示整体与部分之间的关系。
  7. 组合关系(Composition):表示一种强的关联关系,表示整体与部分之间的关系,部分不能独立存在。

UML 类图提供了一种可视化的方式来描述系统的结构和行为,有助于开发人员和设计师更好地理解和沟通系统的设计。它在软件开发过程中起到了重要的指导和文档作用。

多态

多态性的关键在于继承和方法重写。通过继承,子类可以继承父类的方法,并且可以在子类中重新实现这些方法以适应子类的特定需求

js 复制代码
class Shape {
  calculateArea() {
    console.log("This is the base class for shapes.");
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width = width;
    this.height = height;
  }

  calculateArea() {
    console.log("Area of the rectangle:", this.width * this.height);
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }

  calculateArea() {
    console.log("Area of the circle:", Math.PI * this.radius * this.radius);
  }
}

// 多态性的体现
const shapes = [new Rectangle(5, 10), new Circle(3)];

shapes.forEach((shape) => {
  shape.calculateArea();
});

在这个示例中,我们有一个基类 Shape 和两个派生类 RectangleCircleShape 类中定义了一个 calculateArea() 方法,而 RectangleCircle 类都重写了这个方法以计算它们各自形状的面积。

在主程序中,我们创建了一个包含 RectangleCircle 对象的数组 shapes。然后,我们使用 forEach 方法遍历数组中的每个元素,并调用它们的 calculateArea() 方法。

由于 shapes 数组中的每个元素都是 Shape 类型的引用,但实际指向的对象是不同的,所以在调用 calculateArea() 方法时,会根据对象的实际类型调用相应的重写方法。

输出结果将根据每个对象的类型而不同,分别计算矩形和圆的面积。

这个例子展示了 JavaScript 中多态性的特性,允许使用相同的方法名在不同类型的对象上产生不同的行为。这提供了更灵活和可扩展的代码结构。

设计模式只是套路,设计原则是指导思想

设计模式可以被视为一种套路,可以帮助开发人员解决特定类型的问题,并提供了一种经过验证的方法。

而设计原则是指导思想,它们提供了关于如何设计良好的软件架构和代码的指导。设计原则是广泛适用的准则,可以指导开发人员做出合理的设计决策,以实现可维护、可扩展和可重用的代码

以下是一些常见的设计原则:

  1. 单一职责原则(Single Responsibility Principle,SRP):一个类应该只有一个引起它变化的原因,即一个类应该只有一个职责。
  2. 开放封闭原则(Open-Closed Principle,OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。即在不修改现有代码的情况下,通过添加新代码来扩展功能。
  3. 里氏替换原则(Liskov Substitution Principle,LSP):子类对象应该能够替换其父类对象,而不会影响程序的正确性。
  4. 依赖倒置原则(Dependency Inversion Principle,DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于具体实现细节,具体实现细节应该依赖于抽象。
  5. 接口隔离原则(Interface Segregation Principle,ISP):客户端不应该依赖于它不需要的接口。一个类不应该强迫它的客户端依赖于它们不使用的方法。
  6. 迪米特法则(Law of Demeter,LoD):一个对象应该对其他对象有尽可能少的了解,只与最直接的朋友通信。

这些设计原则提供了一些通用的指导原则,帮助开发人员构建具有良好设计和高内聚低耦合的软件系统。设计模式则是在实践中应用这些设计原则的具体实现方式。

场景设计模式

工厂模式、单例模式(全局只允许有一个实例)、观察者模式、迭代器模式、原型模式、装饰器模式、代理模式

工厂模式

什么是工厂模式,它主要解决什么问题

工厂模式是一种创建对象的设计模式,它主要解决了对象的实例化过程与使用过程之间的耦合问题。它通过引入一个工厂类或接口,将对象的创建逻辑封装起来,客户端代码只需要通过工厂来创建对象,而无需直接使用 new 关键字实例化对象

工厂模式的主要目标是将对象的创建与使用分离,以提供更好的灵活性和可维护性。它可以解决以下问题:

  1. 隐藏对象的创建过程:使用工厂模式,客户端代码无需了解具体的对象创建细节,只需要知道如何通过工厂来创建对象。这样可以将对象的创建逻辑封装在工厂中,隐藏起来,使客户端代码更加简洁和易读。
  2. 解耦合:工厂模式通过引入工厂类或接口,将客户端代码与具体的产品类解耦。客户端只需要与工厂进行交互,而不需要直接依赖具体的产品类。这样可以降低代码的耦合度,提高代码的可维护性和扩展性。
  3. 提供灵活性:通过工厂模式,可以根据需要选择不同的工厂来创建不同的产品对象。这样可以在不修改客户端代码的情况下,更换或新增具体的产品类。工厂模式提供了一种可扩展的机制,使系统更具灵活性。
js 复制代码
// 抽象产品接口
interface Product {
  operation(): void
}
// 具体产品类A
class ConcreteProductA implements Product {
  operation(): void {
    console.log('具体产品A的操作')
  }
}
// 具体产品类B
class ConcreteProductB implements Product {
  operation(): void {
    console.log('具体产品B的操作')
  }
}

// 抽象工厂接口
interface Factory {
  creteProduct(): Product
}
// 具体工厂类A
class ConcreteFactoryA implements Factory {
  creteProduct(): Product {
    return new ConcreteProductA();
  }
}
// 具体工厂类B
class ConcreteFactoryB implements Factory {
  creteProduct(): Product {
    return new ConcreteProductB();
  }
}
// 创建具体工厂对象
const factoryA: Factory = new ConcreteFactoryA();
const factoryB: Factory = new ConcreteFactoryB();

// 使用工厂A创建产品A
const productA: Product = factoryA.creteProduct();
productA.operation(); // 输出:具体产品A的操作

// 使用工厂B创建产品B
const productB: Product = factoryB.creteProduct();
productB.operation(); // 输出:具体产品B的操作

这里,我们通过工厂对象的 createProduct() 方法来创建具体的产品对象,并调用产品对象的方法进行操作。通过工厂模式,我们可以根据需要选择不同的工厂来创建不同的产品,而不需要直接关注具体的产品类

工厂模式缺点

工厂模式作为一种常用的设计模式,虽然有很多优点,但也存在一些缺点,包括:

  1. 增加了代码复杂性:引入工厂模式会增加额外的类和接口,增加了代码的复杂性和理解难度。工厂模式需要定义抽象工厂、具体工厂和产品接口等,这些额外的结构和层级可能会使代码变得更加复杂。
  2. 增加了系统的抽象性:工厂模式通过引入抽象工厂和产品接口,将对象的创建过程进行了抽象和封装。这种抽象性可能会导致系统的理解和调试变得困难,特别是对于初学者或新加入的开发人员来说。
  3. 不易于扩展和变化:尽管工厂模式提供了一种灵活的方式来创建对象,但当需要添加新的产品类型时,需要修改工厂类和产品接口的定义,这可能导致代码的修改和重构。这种扩展性的局限性可能会增加代码的维护成本。
  4. 增加了系统的依赖性:使用工厂模式会增加系统中类之间的依赖关系。客户端代码必须依赖于工厂接口和产品接口,这种依赖关系可能会增加系统的耦合度,使得代码更难以理解和修改。
  5. 可能引入过多的工厂类:随着系统的复杂性增加,可能需要引入多个具体工厂类来创建不同类型的产品。这可能导致工厂类的数量增加,使得代码变得冗长和复杂。

尽管工厂模式存在一些缺点,但在适当的场景下仍然是一种有价值的设计模式。它可以提供灵活性、可维护性和可扩展性,尤其在需要将对象的创建过程与使用过程分离,并提供统一的接口来访问对象时,工厂模式是一个有用的选择。

工厂模式的场景-jQuery

在 jQuery 库中,工厂模式被广泛应用于创建和操作 DOM 元素。jQuery 提供了一个全局函数 $,它实际上是一个工厂函数,用于创建 jQuery 对象

下面是一个简单的示例,展示了如何使用 jQuery 的工厂模式来创建和操作 DOM 元素:

js 复制代码
// 创建一个 div 元素
var div = $('<div></div>');

// 添加类名和文本内容
div.addClass('myDiv').text('Hello, jQuery!');

// 将元素添加到文档中的 body 元素中
$('body').append(div);

在上述示例中,$('<div></div>') 使用 $ 工厂函数创建了一个 jQuery 对象,它封装了一个新创建的 <div> 元素。然后,我们可以使用 jQuery 提供的方法来操作这个 jQuery 对象,例如 addClass()text() 方法。最后,通过调用 $('body').append(div) 将这个元素添加到文档中的 <body> 元素中。

通过使用工厂模式,jQuery 提供了一种简洁而灵活的方式来创建和操作 DOM 元素。它隐藏了底层的 DOM 操作细节,使开发人员能够以更简洁的方式编写代码,并提供了丰富的方法和功能来操作 DOM 元素。

除了创建和操作 DOM 元素,jQuery 还使用工厂模式来创建和操作其他对象,例如 AJAX 请求对象、事件对象等。工厂模式使得 jQuery 能够提供一致的接口和易于使用的功能,成为了 Web 开发中常用的工具库之一。

工厂模式的场景-Vue和React的createElement

在 Vue 和 React 中,工厂模式被用于创建虚拟 DOM 元素。虚拟 DOM 元素是用于描述 UI 的 JavaScript 对象,它们最终会被渲染成实际的 DOM 元素。

在 Vue 中,使用 createElement 函数创建虚拟 DOM 元素。createElement 是一个工厂函数,它接受三个参数:标签名、属性对象和子元素。通过调用 createElement 函数,可以创建一个虚拟 DOM 元素。

以下是一个使用 Vue 的 createElement 创建虚拟 DOM 元素的示例:

js 复制代码
new Vue({
  el: '#app',
  render: function(createElement) {
    return createElement('div', { class: 'myDiv' }, 'Hello, Vue!');
  }
});

在上述示例中,render 函数使用 createElement 工厂函数创建了一个虚拟 DOM 元素 <div>,它具有类名为 'myDiv',并且文本内容为 'Hello, Vue!'。这个虚拟 DOM 元素最终会被渲染成实际的 DOM 元素,并插入到具有 id 为 'app' 的元素中。

类似地,在 React 中,使用 React.createElement 函数创建虚拟 DOM 元素。React.createElement 也是一个工厂函数,它接受三个参数:标签名或组件、属性对象和子元素。通过调用 React.createElement 函数,可以创建一个虚拟 DOM 元素。

以下是一个使用 React 的 createElement 创建虚拟 DOM 元素的示例:

js 复制代码
ReactDOM.render( 
    React.createElement('div', { className: 'myDiv' }, 'Hello, React!'),
    document.getElementById('app')
);

在上述示例中,React.createElement 工厂函数创建了一个虚拟 DOM 元素 <div>,它具有类名为 'myDiv',并且文本内容为 'Hello, React!'。通过调用 ReactDOM.render 将这个虚拟 DOM 元素渲染成实际的 DOM 元素,并插入到具有 id 为 'app' 的元素中。

通过使用工厂模式,Vue 和 React 提供了一种简洁而灵活的方式来创建虚拟 DOM 元素。工厂模式隐藏了底层的 DOM 操作细节,使开发人员能够以声明式的方式描述 UI,并提供了丰富的功能和组件来构建复杂的应用程序界面。

单例模式 - 全局只允许有一个实例,多则出错

什么是单例模式,它主要解决什么问题

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。

单例模式解决的主要问题是控制对象的实例化过程,确保在整个应用程序中只有一个实例存在。这在某些情况下是很有用的,例如:

  1. 资源共享:单例模式可以用来管理共享的资源,例如数据库连接池、线程池等。通过使用单例模式,可以确保所有的请求都使用同一个资源实例,避免资源的重复创建和管理。
  2. 对象跨越多个模块的访问:单例模式可以提供一个全局访问点,使得不同模块中的代码可以方便地访问同一个对象实例。这在需要共享数据或协调不同模块之间操作时非常有用。
  3. 控制实例数量:有些情况下,我们需要限制一个类的实例数量,确保只有一个实例存在。例如,某些设备驱动程序只允许有一个实例,或者某些配置信息只需要加载一次。

单例模式通过将类的实例化过程封装在类内部,并提供一个静态方法或属性来访问该实例,确保只有一个实例被创建并全局可访问。这样可以简化代码的使用方式,避免重复创建实例,同时提供了一种集中管理和控制对象实例的方式。

然而,单例模式也有一些缺点,例如可能引入全局状态和共享状态的问题,使得代码的依赖关系变得复杂。因此,在使用单例模式时需要谨慎考虑,并确保它真正解决了问题并符合应用程序的设计需求。

单例模式缺点

当使用单例模式时,需要注意以下一些潜在的缺点:

  1. 难以扩展和测试:由于单例模式创建了一个全局唯一的实例,它可能会导致代码的扩展和测试变得困难。其他部分的代码依赖于单例对象,如果需要修改或替换该对象,可能需要修改大量的代码。
  2. 引入全局状态:单例模式将实例对象设为全局可访问,这可能导致全局状态的引入。全局状态的管理变得复杂,可能会增加代码的耦合性和维护难度。
  3. 增加代码的耦合性:使用单例模式可能会增加代码的耦合性。其他部分的代码可能会直接依赖于单例对象,这使得单例对象的修改变得困难,可能需要同时修改依赖于该对象的其他代码。
  4. 可能引入并发问题:如果在多线程或异步环境中使用单例模式,可能会引入并发问题。当多个线程或任务同时访问单例对象时,需要考虑线程安全性和同步机制,以避免数据竞争和不一致的状态。
  5. 难以进行单元测试:由于单例对象通常在整个应用程序中被共享和访问,它可能会导致单元测试变得困难。在单元测试中,我们通常希望能够独立地测试每个模块,但单例对象的全局可访问性可能会干扰测试的隔离性。

下面是一个例子,展示了单例模式的一些缺点:

js 复制代码
var Singleton = (function() {
  var instance;

  function Singleton() {
    this.counter = 0;
  }

  Singleton.prototype.incrementCounter = function() {
    this.counter++;
  };

  return {
    getInstance: function() {
      if (!instance) {
        instance = new Singleton();
      }
      return instance;
    }
  };
})();

var singletonInstance1 = Singleton.getInstance();
var singletonInstance2 = Singleton.getInstance();

singletonInstance1.incrementCounter();
console.log(singletonInstance2.counter); // 输出: 1

singletonInstance2.incrementCounter();
console.log(singletonInstance1.counter); // 输出: 2

在上述示例中,我们使用单例模式创建了一个计数器对象。然而,由于单例对象是全局共享的,对计数器对象的操作会影响到其他部分的代码。这可能导致并发问题和难以预测的行为。此外,由于全局状态的引入,单元测试变得困难,无法独立地测试每个模块。

单例模式示例

js 复制代码
// 立即执行函数创建单例对象
var Singleton = (function(){
  // 单例实例
  var instance;
  // 私有属性和方法
  function initialize() {
    var privateVariable = "私有属性"
    function privateMethod() {
      console.log("私有方法")
    }
    return {
      // 共有属性和方法
      publicMethod: function() {
        console.log("公有方法")
      },
      publicVariable: "公有属性",
      getPrivateVariable: function() {
        return privateVariable;
      }
    }
  }
  // 获取单例的实例和方法
  return {
    getInstance: function() {
      if(!instance) {
        instance = initialize()
      }
      return instance;
    }
  }
})()

- 当使用 TypeScript 实现单例模式时,我们可以通过一个示例来说明其用法。假设我们有一个日志记录器,我们希望在整个应用程序中共享同一个日志记录器实例。

ts 复制代码
class Logger {
  private static instance: Logger;
  private logs: string[];

  private constructor() {
    this.logs = [];
  }

  public static getInstance(): Logger {
    if (!Logger.instance) {
      Logger.instance = new Logger();
    }
    return Logger.instance;
  }

  public log(message: string): void {
    this.logs.push(message);
  }

  public printLogs(): void {
    console.log(this.logs);
  }
}

// 使用单例日志记录器
const logger1 = Logger.getInstance();
const logger2 = Logger.getInstance();

logger1.log("Message 1");
logger2.log("Message 2");

logger1.printLogs(); // 输出: ["Message 1", "Message 2"]
logger2.printLogs(); // 输出: ["Message 1", "Message 2"]

在上述示例中,我们定义了一个名为 Logger 的类,它实现了单例模式。

  • 类中的 instance 是一个私有的静态成员,用于存储单例实例。
  • 构造函数 constructor 被设为私有,以防止直接实例化 Logger 类。
  • getInstance 是一个公共的静态方法,用于获取单例实例。如果实例不存在,则创建一个新的实例并将其赋值给 instance,然后返回该实例。如果 instance 已经存在,直接返回它。
  • logs 是一个私有成员,用于存储日志消息。
  • log 是一个公共方法,用于向日志记录器添加消息。
  • printLogs 是一个公共方法,用于打印日志消息。

在示例中,我们通过调用 Logger.getInstance() 方法来获取单例实例。我们创建了两个变量 logger1logger2,它们实际上引用的是同一个日志记录器实例。

然后,我们分别向 logger1logger2 添加了两条日志消息。由于它们引用的是同一个实例,这些日志消息都会被添加到同一个日志记录器中。

最后,我们分别调用 logger1.printLogs()logger2.printLogs() 方法来打印日志消息。可以看到,两个日志记录器实例中都包含了相同的日志消息。

这个示例展示了如何使用 TypeScript 实现单例模式,并通过共享单例实例在整个应用程序中进行日志记录。这种方式可以确保我们在应用程序中共享同一个日志记录器,方便统一管理和查看日志消息。

单例模式的场景-登录框

ts 复制代码
class LoginBox {
  private static instance: LoginBox;
  private loggedIn: boolean;

  private constructor() {
    this.loggedIn = false;
  }

  public static getInstance(): LoginBox {
    if (!LoginBox.instance) {
      LoginBox.instance = new LoginBox();
    }
    return LoginBox.instance;
  }

  public login(username: string, password: string): void {
    // 执行登录逻辑,验证用户名和密码
    // ...

    // 登录成功
    this.loggedIn = true;
    console.log("User logged in");
  }

  public logout(): void {
    // 执行登出逻辑
    // ...

    // 登出成功
    this.loggedIn = false;
    console.log("User logged out");
  }

  public isLoggedIn(): boolean {
    return this.loggedIn;
  }
}

// 使用登录框
const loginBox1 = LoginBox.getInstance();
const loginBox2 = LoginBox.getInstance();

loginBox1.login("username", "password");
console.log(loginBox1.isLoggedIn()); // 输出: true
console.log(loginBox2.isLoggedIn()); // 输出: true

loginBox2.logout();
console.log(loginBox1.isLoggedIn()); // 输出: false
console.log(loginBox2.isLoggedIn()); // 输出: false

在上述示例中,我们定义了一个名为 LoginBox 的类,它实现了单例模式。

  • 类中的 instance 是一个私有的静态成员,用于存储单例实例。
  • 构造函数 constructor 被设为私有,以防止直接实例化 LoginBox 类。
  • getInstance 是一个公共的静态方法,用于获取单例实例。如果实例不存在,则创建一个新的实例并将其赋值给 instance,然后返回该实例。如果 instance 已经存在,直接返回它。
  • loggedIn 是一个私有成员,用于表示用户的登录状态。
  • login 是一个公共方法,用于执行用户登录逻辑,并更新登录状态。
  • logout 是一个公共方法,用于执行用户登出逻辑,并更新登录状态。
  • isLoggedIn 是一个公共方法,用于检查用户的登录状态。

在示例中,我们通过调用 LoginBox.getInstance() 方法来获取单例实例。我们创建了两个变量 loginBox1loginBox2,它们实际上引用的是同一个登录框实例。

然后,我们通过调用 loginBox1.login("username", "password") 方法进行用户登录。由于 loginBox1loginBox2 引用的是同一个实例,因此它们的登录状态是相互影响的。

最后,我们分别调用 loginBox1.isLoggedIn()loginBox2.isLoggedIn() 方法来检查登录状态。可以看到,无论是通过 loginBox1 还是 loginBox2 访问,它们都返回相同的登录状态。

这个示例展示了如何使用单例模式实现登录框,并确保在整个应用程序中只存在一个登录框实例。这样可以保证用户的登录状态在全局范围内的一致性,并且方便地管理用户的登录和登出操作。

观察者模式

什么是观察者模式,它解决什么问题

观察者模式(Observer Pattern)是一种行为设计模式,用于在对象之间建立一种一对多的依赖关系,当一个对象的状态发生改变时,它的所有依赖对象都会收到通知并自动更新。

观察者模式解决的问题是对象之间的解耦和通信问题。当多个对象之间存在一种依赖关系,一个对象的状态改变需要通知其他对象进行相应的处理时,使用观察者模式可以有效地实现这种通信机制。

以下是观察者模式的几个关键角色:

  • Subject(主题) :也称为被观察者或可观察对象,它维护一组观察者对象,并在状态发生改变时通知观察者。
  • Observer(观察者) :也称为订阅者或监听者,它定义了一个接口,用于接收主题的通知并进行相应的处理。
  • ConcreteSubject(具体主题) :实现主题接口,维护具体的状态,并在状态改变时通知观察者。
  • ConcreteObserver(具体观察者) :实现观察者接口,定义具体的处理逻辑,接收主题的通知并进行相应的操作。

观察者模式的工作原理如下:

  1. 主题对象(Subject)维护了一个观察者列表,并提供方法用于注册、注销和通知观察者。
  2. 观察者对象(Observer)通过注册方法将自身添加到主题对象的观察者列表中,以便接收通知。
  3. 当主题对象的状态发生改变时,它会遍历观察者列表,并调用每个观察者的通知方法,将状态改变的信息传递给观察者。
  4. 观察者收到通知后,根据接收到的信息进行相应的处理,可能会更新自身的状态或执行其他操作。

观察者模式的优点包括:

  • 解耦性:主题和观察者之间的关系是松散耦合的,它们可以独立地进行扩展和修改,而不会相互影响。
  • 可维护性:由于对象之间的依赖关系明确,代码的维护和调试更加容易。
  • 可扩展性:可以很方便地添加新的观察者对象,而不需要修改现有的代码。

观察者模式适用于以下情况:

  • 当一个对象的改变需要同时影响其他对象,并且不希望对象之间紧密耦合时。
  • 当一个对象的改变需要通知一组对象,而不知道具体有多少个对象需要通知时。
  • 当需要确保对象之间的一致性,避免手动维护对象之间的关联关系时。

总而言之,观察者模式提供了一种松散耦合的通信机制,使得对象之间的依赖关系更加灵活和可扩展。它解决了对象之间解耦和通信的问题,提高了代码的可维护性和可扩展性。

相关推荐
小白学习日记44 分钟前
【复习】HTML常用标签<table>
前端·html
丁总学Java1 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele2 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
懒羊羊大王呀2 小时前
CSS——属性值计算
前端·css
DOKE2 小时前
VSCode终端:提升命令行使用体验
前端
xgq2 小时前
使用File System Access API 直接读写本地文件
前端·javascript·面试
用户3157476081352 小时前
前端之路-了解原型和原型链
前端
永远不打烊2 小时前
librtmp 原生API做直播推流
前端
北极小狐2 小时前
浏览器事件处理机制:从硬件中断到事件驱动
前端
无咎.lsy2 小时前
vue之vuex的使用及举例
前端·javascript·vue.js